Text
                    1
В КОМПЛЕКТЕ
с CD-ROM
Ъ
X
Джерри Пик, Тим О'Райли, МайкЛукидис
Ф
O'REILLY4


UNIX инструментальные средства
>шшш0р:шттт :ШШтш яг UNIX OWER TOOLS Jerry Peek, Tim O'Reilly, Mike Loukides, and other authors of the Nutshell Handbooks, including Linda Mui, Dale Dougherty, Larry Wall, and Randal Schwartz, plus Usenet contributors including Chris Torek, Jonathan Kamens, Bruce Barnett, and Tom Christiansen O'REILIT Cambridge • Koln • Paris • Sebastopol • Tokyo
Джерри Пик, Тим О'Райпи, Майк Лукидис шшш Перевод с английского под редакцией М.В. Коломыцева BHV, Киев. 1999
УДК 681.3.06 Джерри Пик, Тим О'Райли, Майк Лукидис UNIX: инструментальные средства: Пер. с англ. — К.: Издательская группа BHV, 1999. — 944 с. ISBN 966-552-020-2 . Издание содержит советы экспертов UNIX, взятые из книг по данной тематике серии "Nutshell Handbook" фирмы O'Reilly и заметок в Usenet, а также материал, специально написанный для данного пособия различными авторами. Рассмотрено множество утилит POSIX, включая GNU-версии. Подробно описаны интерпретаторы bash и tcsh, но основное внимание акцентируется на ключевых концепциях интерпретаторов sh и csh, знание которых позволит вам одинаково успешно работать со всеми интерпретаторами UNIX. На компакт-диск, прилагаемый к книге, авторы поместили тысячи советов, сценариев и полезных утилит, которые помогут сделать работу в UNIX более легкой и эффективной. Помимо этого, на компакт-диске записаны мощные бесплатные программы, способные значительно расширить возможности стандартного набора команд UNIX. Все программы скомпилированы для платформ Solaris, SunOS, Digital UNIX, IBM AIX, HP/UX, Linux, Intel и SCO UNIX. Предназначена для широкого круга читателей — от новичков, которые смогут детально ознакомиться с основными возможностями операционной системы UNIX, до профессионалов, узнающих много полезного благодаря советам десятков специалистов в данной области. Все права защищены. Авторизованный перевод выполнен с издания на английском языке, выпущенного "OReilly & Associates, Inc.". ISBN 1-56592-260-3 © O'Reilly & Associates, Inc., 1997 ISBN 966-552-020-2 © Издательская группа BHV, Издательство "Ирина", 1999
Как пользоваться книгой Верхний колонтитул В верхних колонтитулах указываются номера первого и последнего параграфов на развороте. Номер параграфа Первые две цифры указывают, ■в какой главе находится параграф, а вторые две являются номером параграфа в главе. Номера параграфов используются в перекрестных ссылках во всей книге. N •30.19 Когда изменения нужно произвести в текущей строке и в следующих пяти, воспользуйтесь командой :.,+5«/\<./\u*/g Чтобы в каждом слове сделать первые буквы прописными (параметр \и), а все остальные — строчными (параметр \L), выполните команду 10 :s/\<\(.\)\([A-Za-z]«\)\>/\u\l\L\2/g Последняя команда не делает строчными начальные буквы слов, которые стоят после дефисов (как в слове CD-ROM) или после апострофов (как в слове O'Reilly). Объясняется это тем, что. шаблон [A-Za-z] *\) \> задает только те слова, в которых все символы, начиная со второго и заканчивая последним, являются буквами. При желании в шаблон можно добавить дефис или апостроф — в таком случае заданное выражение будет охватывать большее количество слов. Для набора указанных команд приходится прикладывать немало усилий. Если вы часто их применяете, воспользуйтесь возможностью переназначения клавиш ()1.ш). - JP 30.18 Автоматическая установка опций редактора w для отдельных файлов Перекрестные ссылки и предложении Чтобы получить больше информации по теме, название которой подчеркнуто, прочтите параграф, номер'Которого приведен в скобках. Резюме Резюме встречаются в книге повсеместно. В них подводятся итоги и указывается, в каких параграфах содержатся примеры и пояснения по соответствующим темам. В файле .ехгс <4.щ можно установить опции редактора W для всех файлов или для файлов из отдельного каталога ром). Ниже перечислены другие способы задания пользовательских ус трежнему существует возможность задействовать строки режима (modelines) (».i». По причинам безопасности их использование нежелательно, но при осторожном их применении проблем не должно быть. Строки режима позволяют сохранять команды установок для каждого редактируемого файла в самом файле. * Для каждого подлежащего редактированию файла вместо использования строк режима можно создать отдельные файлы установок. В этом случае отпадут проблемы, связанные с безопасностью при использовании строк режима, поскольку файлы установок можно сделать доступными только для чтения (см. параграф 30.21). • В параграфе 30.20 рассказано, как путем ввода' соответствующей команды в интерпретаторе shell выбирается один из нескольких наборов установок. Все команды редактора и", которые будут вызываться впоследствии, должны использовать эти установки. Выполнить новые установки ыожно в любое время. - JP 30.19 Строки режима: ошибка или полезное свойство? Пиктограмма "шуруп" Обращайте внимание на этот значок, чтобы не попасть впросак. В некоторых версиях редакторов W и ех присутствует опция modelme (строка режима) или modelines (строки режима). Если опция установлена в файле .ехгс (жш), команды установок могут запоминаться в начале или конце любого редактируемого файла. При запуске редактор будет читать и вылолнять эти команды установок. Это очень напоминает тот случай, когда для каждого редактируемого файла имеется отдельный файл .ехгс. Нижний колонтитул Колонтитулы помогут быстро сориентироваться при поиске материала в книге. В нижних колонтитулах, расположенных на нечетных страницах, указываются названия глав, а в нижних колонтитулах на четных страницах — названия частей. ._ С * Использование строк режима может породить проблемы, саязанные с обеспечением безопасности. Подумайте, с какими неприятностями можно столкнуться, если какой-то злобный пользователь отредактирует ввши файлы и изменит в них строки режима. Большинство новых версий редактора w по умолчанию отменяют действие строк режима. ОеЫтивсти р«дмп>/м W 469
Пиктограмма "бомба" Увидев на поле этот значок, прочтите параграф, номер которого указан под ним В таких параграфах дается объяснение трудностей, которые могут возникнуть при использовании приема илн сценария, описанного в текущем параграфе. 5.0S изменить смысл оператора сравнения, следует поставить перед ним восклицательный Например, выражение !@1200 служит для обозначения любой скорости, кроме 1200 бол. iell для этого употребляется обратная косая черта \!@1200 (им).) сазанин нескольких опций -от; будет выбрана первая подходящая. Если ни одна опция не ит, выбирается последнее значение, указанное в строке без опции -от (см. предыдущий пример). Если тип терминала не указан, используется тип, записанный в файле ttytype. Рассмотренные способы изменения типа терминала не всегда срабатывают. Причины этого описаны в параграфе 42.03. В параграфе 41.09 приведен сценарий для настройки терминала. — TOR, из книги "termcap & terminfo" издательства O'Reilly & Associates Инициалы автора Каждый параграф завершается инициалами авюра. Полные именл приведены в предисловии. [38.08] Установка переменной TERMCAP с помои Пользователи С shell смогут обратиться к еще более мощной Опция -s заставляет ее посылать на стандартный выпод послед для установки не только переменной TERM, но и переменной действительным содержимым записи в файле 1егтсар)ЛБаза данных: termcap Это ускоряет запуск программ, использующих файл lermcap: им не нужно больше просматривать этот файл в поисках нужной записи — она под рукой. Вызов команды tsel осуществляется следующим образом: ^L set noglob eval *t3et -Q ■3 другие_опцки Перекрестные ссыпки в примерах кода Во фрагментах кода ссылки приводятся на полях. ЧтпАм I понять назначение команды tsel, давайте посмотрим, что она выводит на экран, если строку без оператора eval: :set -Q -• wySO noglob; :env TERM wy50; Tlitenv TERMCAP 'n9|uy50:HX24:co#80:am:bs:bw:ul:\ 10.08 I Компиляция и изменение режима доступа к программе на С. # Не используйте расширение имени файла .с. а11аз С 'eval "if (\>* — *.с) then \\ echo "С quitting: no .с on ond of \!* please/" \\ else \\ if <-e \!*) mv \!* \!*.old \\ echo \!*.c SENT TO ее \\ ее -S \!*.с -о \!» \\ if (-e \!*) chmod 311 \1* \\ endif "' ., JP Компакт-диск я расстановка кавычек в псевдонимах не приведенный в книге, можно взять с прилагаемого компакт-диска. Задайте для программы инсталляции имя программы, указанное под пиктограммой. Подробности вы найдете в параграфе 52.05. л шил киманду пик) гь кавычки и обратную косую черту в определении псевдонима довольно Текст сценария^ияи^программы иа С, „йн (Dan Bernstein) написал две команды, makealias и quote, которые i этим, i псевдоним makealias, чтобы не заниматься защитой символов ! и *: l,/!*/d' I lees •Is I aed '\"l,/\!*/d'\' I leas' •alias mycat, а также строку, начинающуюся с команды cat, и получил определение псевдонима со всеми необходимыми символами кавычек, и обратной косой черты. Корректное определение псевдонима направляется на стандартный вывод. Полученную строку нужно использовать при определении псевдонима.* А вот и сами команды quote и makealias: alias quote "/Мп/sed -е ' з/\\ !/\\\\\!/g' \\ -е 'з/'\\\"/'\\\'\\\\\\\'\\\"/д> \\ -е 's/V\"/' -e 's/"\$"/'\"/"' alia3 makealias "quote I /bin/sed 's/"/alias \!:1 /' \!:2*" Это довольно громоздко, но работает. — JIK, из телеконференции comp.untx.questio/ts в Usenet, 17 февраля 1991 г. яет хы. / с по вы его зо- 79
Предисловие Структура книги Технические книги бывают скучными. Но эта книга необычна! Она является и альманахом, и журналом, и гипертекстовой базой данных одновременно. Мы не располагали материал в строгом порядке, рассчитывая, что вы прочтете все от корки до корки. Начните чтение с любого места. Прочтите то, что вас интересует. (Но прежде ознакомьтесь с предисловием, инструкциями по использованию книги и главой 1. Далее можно читать в каком угодно порядке.) Данная книга отличается практической направленностью. Авторы не ставили перед собой цель всесторонне раскрыть ключевые понятия. Здесь собраны описания типичных проблем и показаны пути их решения. Хотя книга не рассчитана на чтение в строгой последовательности, она разбита на главы по тематическому признаку. Поэтому если вы захотите найти материал определенной тематики, то сможете воспользоваться для этого традиционным средством — оглавлением. Многие главы снабжены отдельными оглавлениями, представляющими собой блоки текста в рамке. Издание включает также предметный указатель, благодаря которому вы сможете быстро находить представляющие для вас интерес выдержки, не просматривая целые параграфы. Книга состоит из кратких параграфов (большинство не занимают более одной страницы). Параграфы пронумерованы в пределах каждой главы. Не все параграфы выполнены в стиле "как сделать...". Некоторые представляют теоретическую ценность. Включенные в книгу параграфы не перенасыщены определениями используемых понятий. Во многих случаях определение замещено "гиперссылкой", указывающей, где получить дополнительную информацию. Если такая информация не нужна, ссылку можно проигнорировать. В книге используются ссылки двух видов — в предложениях и на полях. Программы на компакт-диске Книга содержит описания сценариев и бесплатных программ, предоставляемых на прилагаемом компакт-диске. Параграф, посвященный программе или файлу на компакт-диске, обозначен его пиктограммой. Чтобы получить одну их таких программ, используйте наш сценарий install (52.05). (Эта перекрестная ссылка означает, что сценарий инсталляции install описан в параграфе 5 главы 52.) О версиях UNIX Между различными версиями UNIX есть много общего. Однако практически невозможно написать книгу, правильно освещающую все детали каждой версии. В тех случаях, когда мы знаем, что из-за значительных различий между версиями могут возникнуть проблемы, мы помещаем соответствующее примечание. В других случаях мы вынуждены использовать осторожное замечание типа "в некоторых версиях...", не указывая, в каких именно версиях. Встретив подобное замечание, можно поступить следующим образом. • Если команда или функция ничего не уничтожит даже в случае неправильного выполнения, попробуйте запустить ее. Экспериментировать с командой rm, удаляющей файлы, конечно, не стоит. А вот команда cat, отображающая файлы, не причинит никакого вреда, могут лишь не проявиться некоторые ее возможности. • Просмотрите диалоговое руководство (so.oi) или печатное руководство поставщика, хотя даже эти документы могут содержать неточности. Например, ваш системный администратор может установить версию команды, которая работает по-другому, и не обновить при этом диалоговое руководство. (Команды which (50.0s) и whereiz (4.10) помогут найти решение проблемы самостоятельно при условии, что вы знакомы с деталями организации файловой системы.) Внимательно относитесь к "общим" руководствам, приобретаемым в магазине. UNIX имеет множество версий, и купленное руководство может не подойти для вашей версии. Предисловие 7
• Проконсультируйтесь у своего системного администратора или другого специалиста, прежде чем использовать потенциально опасную команду. Перекрестные ссылки Перекрестная ссылка может включать одно слово, например имя команды (tar ao.os)), или выражение (запись служебной информации в стандартный поток вывода (w.osj). В первом случае ссылка, скорее всего, организована на параграф, полностью посвященный выделенному понятию, а во втором — на параграф, содержащий краткие справки по указанному вопросу. Перекрестные ссылки не содержат полного перечня параграфов, посвященных данной теме. При их создании мы старались выбирать параграфы, дающие наиболее полную информацию или наиболее точные определения. Что нового во втором изцании С момента выпуска первого издания книги в начале 90-х в UNIX был внесен ряд коренных изменений, которые мы постарались учесть при подготовке второго издания. Переработке подверглись около 550 параграфов из 800 исходных. Мы учли сотни читательских пожеланий и замечаний. Компакт-диск содержит обновленные сценарии и файлы, а также исполняемые файлы для современных UNIX-платформ, в том числе для Linux. Самые важные изменения коснулись следующего: • Различия между UNIX-системами System V и BSD все еще заметны, однако менее значительны. Рассматривая опции и команды, мы сместили акцент в сторону утилит, соответствующих стандарту POSIX, в том числе в сторону GNU-версий этих утилит (которые сегодня близки к POSIX^jro обладают и другими возможностями). • Интерпретаторы tcsh и bash стали более распространенными и сегодня заслуживают гораздо большего, чем примечания, посвящавшиеся им ранее. Поскольку в интерпретаторе bash объединены мощные возможности программирования Bourne shell и удобные интерактивные возможности csh (и не только они!), мы уделили ему больше внимания, чем tcsh. Однако из нашей книги много полезного почерпнут и пользователи tcsh: большое внимание уделено csh, а многое, что написано о csh, относится и к tcsh. Сохранен акцент на основных понятиях sh и csh, что сделает книгу полезной для пользователей всех видов shell. • Изъята глава, посвященная утилите awk, поскольку в настоящее время язык Perl играет намного большую роль, чем утилита awk. Некоторые параграфы этой главы, в том числе удобный девятистраничный справочник по утилите awk (зз.п), включены в другие главы. Исключена краткая глава о средствах обеспечения безопасности. Эта тема является настолько важной, что раскрыть ее в небольшой главе невозможно. Однако книга содержит много ценных советов относительно организации файловой системы и установления прав доступа. Обозначения в тексте и шрифтовые выделения Курсив Используется для выделения имен всех.утилит UNIX, опций командной строки, каталогов и имен файлов, а также новых терминов при первом включении в текст. Применяется в пояснениях к программам и примерам. Полужирный шрифт Используется для выделения ключевых понятий, на которых необходимо акцентировать внимание читателя. Моноширинный шрифт Используется для выделения фрагментов кода в примерах, а также ссылок на примеры или фрагменты кода в основном тексте. Моноширинный Этим шрифтом в примерах выделены команды или текстовые полужирный шрифт фрагменты, которые должны быть набраны пользователем. Курсивный или Этим шрифтом во фрагментах кода и примерах выделены полужирный курсивный переменные, для которых должна быть выполнена контекстно- моноширинный шрифт зависимая подстановка (например, переменная имя- файла должна быть заменена действительным именем файла).
имя_функции(г\) CTRL □ TAB Ссылка на страницу раздела п в диалоговом руководстве (например, getopK2) относится к man-странице getoptb разделе 3). Приглашение С shell. Приглашение Bourne shell. Знак улыбки, означающий: "Не принимайте это всерьез". Этот символ появился в Usenet о.зз) и стал общепринятым (si.iv. Троеточие заменяет текст (обычно результат работы программы), опущенный с целью повышения наглядности или экономии места. Управляющий символ. Чтобы ввести, например, [CTRL-d], нажмите клавиши [Ctrl] и [d]. Для управляющих символов регистр не имеет значения, и "d" может относиться как к верхнему, так и к нижнему регистру. Запись AD также означает fCTRL-dl Иногда сочетание клавиш заключено в рамку (например, 1 CTRL-d [); в таком случае мы хотам показать более выразительно, что именно нужно набрать. В некоторых примерах применяется для обозначения пробела. В некоторых примерах применяется вместо символа табуляции. Авторы "У этой книги три основных автора — Джерри Пик (Jerry Peek), Тим О'Райли (Tim O'Reilly) и Майк Лукидис (Mike Loukides). Однако мы получили также материалы от тех, кто в свре время разместил в Usenet хороший совет, от авторов справочников.серии Nutshell Handbooks, разрешивших нам взять материал из их книг, а также от разработчиков Программных пакетов, позволивших позаимствовать несколько параграфов из файлов README и другой документации. В конце каждого параграфа приведены инициалы автора (авторов), расшифровку которых вы найдете в следующей таблице. AD Angus Duggan AF Aeleen Frisch. . AN Adrian Nye BA . Brandon S. Allbery BB Bruce Bamett BR CT DC DD DG DH DL Bill Rosenblatt Chris Torek Debra Cameron Dak Dougherty Daniel Gilly Dave Hitz Don Libes 43.23,43.24 22.04, 38.05, 38.10, 39.07, 40.12, 40.13, 40!l4"'.- 11.03 41.05 8.16, 8.17, 8.18, 8.19, 16.21, 16.24, 16.25, 17.02, 17.04, 17.10, 17.13, 17.14, 17.15, 17.16,- 17.25, 20.02; 20.03, 20.05, 20.7, 20.08, 20.12, 20.13, 20.14, 22.02, 22,09, 23.06, 26.02, 26.04, 31.11, 31.13 11.13, 32.01, 32.03, 32.06, 32.12, 44.18 7.03, 8.12, 14.12, 16.05, 17.22, 17.24, 20.06, 22.20,23.18, 23.22, 24.03, 24.16, 30.03, 30.34, 33.07, 34.23, 38.07, 38.17, 41.02, 41.06, 45.02, 45.04, 45.14, 45.24, 46.06,46.07 32.01, 32.03, 32.04, 32.05, 32.11, 32.12 1.06, 1.28, 26.01, 26.03, 26.05, 26.06, 26.07, 26.08 28.01, 28.02, 29.01, 29.03, 29.04, 30.10, 30.31, 34.03, 3405, 34.06, 34.07, 34.08, 34.09, 34.10, 34.14, 34.15, 34.16, 34.18, 34.20, 43,02, 43.18, 43.-19, 48 8.05,9.25, 10.02, 15.07, 15.08, 16.12, 25.02, 26.02, 26.09, 28.12, 28.15, 29.06, 30.15, 30.31, 31.02, 31.06, 31.08, 33.04, 33.11, 34.24, 35.10, 35.14,35.18, 3520, 43.07, 47.03, 47.04, 47.06 31.15 9.26 45.26, , 27.02, 33.02, 34.11, 17.05, 20.04, 23.07, 23.19, 38,16, 46.02, 27.05, 33.12, 34.13, .10 26.10, 33.03, 45.28,
DR DS EK EP GS GU HS JIK JM JP JS LK LL LM Daniel Romike Daniel Smith Eileen Kramer Eric Pearce Gene Spafford Greg Ubben Henry Spencer Jonathan I. Kamens Jeff Moskow Jerry Peek John Strang Lar Kaufman Linda Lamb Linda Mui 9.06, 11.07 2.13, 10.05, 16.09, 45.32 30.02, 31.01 52.05, 52.06, 52.08 22.17 27.11, 27.14, 34.17 21.04 10.08, 19.07, 22.14, 23.09, 24.18, 27.12, 35.09, 38.03, 38.12, 51.11 52.09 5.02, 30.28, 41.10, 41.11, 41.12 29.05 30.04, 30.05, 30.09, 30.11, 30.12, 31.02, 32.06 3.07, 5.05, 11.14, 12.09, 13.11, 16.20, 18.07, 18.11, 22.15, 22.16, 25.18, 27.09, 27.20, 28.05, 28.08, 33.10, 35.23, 43.14, 43.17, 43.21, 43.25, 45.11, 48.08, 48.09, 48.11, 48.12, 49.05, 51.07, 52.04, 52.08 LW MAL ML MS RS SG SW TC TOR UM Larry Wall Maarten Litmaath Mike Loukides Mike Stansbery Randal Schwartz Simson Garfinkel Sun Wu Tom Christiansen Tim O'Reilly Udi Manber 18.08, 13.06, 49.04 18.08, 22.17 27.08 13.02, 27.08 18.10, 50.08 18.10, 31.09, 18.14, 18.14, 37.04, 27.13, 27.13, 47.02 37.02, 37.03 37.02. 37.03 При составлении книги мы использовали взятые из Usenet заметки, авторы которых и не подозревали о том, что эти материалы попадут в книгу, а просто делились тем, что узнали. В таких случаях мы постарались привести все возможные ссылки. Вопросы и замечаний Авторы заранее признательны читателям, направившим в их адрес сообщения с замечаниями и предложениями по улучшению книги: O'Reilly & Associates, Inc. 101. Morris Street Sebastopol, CA 95472 USA Телефоны в США и Канаде: 1-800-998-9938 Международный телефон: 1-707-829-0515 Факс: 1-707-829-0104 Электронная почта: bookquestions@oreilly.com Благодарности Эта книга не вышла бы в свет без содействия Рона Петруши (Ron Petrusha). Работая менеджером по вопросам закупки технической литературы в компании Golden-Lee, крупнейшем поставщике книг, он заметил нас сразу же, как только в середине 80-х мы начали публиковать справочники серии Nutshell Handbooks. Он был одним из наших первых помощников, перед которым мы в долгу. Поэтому, когда он стал редактором в издательстве Bantam (чей компьютерный отдел впоследствии был приобретен издательством Random House), мы отнеслись со всей серьезностью к его предложениям о совместной деятельности.
Рон предложил совместно выпустить книгу о UNIX в серии Power Tools издательства Bantam. Это было взаимовыгодное предложение. Издательство Bantam в нашем лице получало специалистов с хорошей репутацией и большим опытом в области UNIX. Перед нами же открывалась блестящая возможность приобретения опыта работы с массовым читателем, а также продолжения популярной серии Power Tools. Каким же должно быть информационное наполнение новой книги? У книги издательства Bantam DOS Power Tools, послужившей для нас образцом, было две особенности, которые мы решили унаследовать: углубленное рассмотрение недокументированных системных возможностей и большая коллекция бесплатных сценариев и утилит. Однако нам не хотелось создавать книгу, которая являлась бы повторением уже имеющихся на рынке книг, где главы о каждом из основных инструментальных средств UNIX следуют Одна за другой в предсказуемом порядке. Конечно, мы стремились предоставить читателям ценную техническую информацию об утилитах UNIX, но более важной задачей было показать, как при помощи набора утилит решить ту или иную проблему. Кроме того, в Противовес многочисленным учебным изданиям по UNIX нам хотелось создать не только содержательную, но и живую книгу. "Печатный гипертекст" — одна из особенностей нашей книги — является не только моей заслугой, но и заслугой Дэйла Дохерти (Dale Dougherty). Дэйл несколько лет работал над гипертекстами и интерактивным доступом к информации, и я постарался привлечь его к работе над проектом. Между нами возникла здоровая конкуренция: мы пытались "обскакать" друг друга, предлагая новаторские идеи относительно структуры книги. По истечении двух недель размышлений и обмена электронной почтой Дэйл, Майк Лукидис и я выработали главные черты книги. Мы думали, что сможем быстро скомпоновать ее, извлекая из многих существующих книг ценные рекомендации. К сожалению, ни у кого из нас не нашлось для этого достаточно времени, и книга, казалось, так и не увидит свет. (Майк был единственным, кто хоть что-то закончил писать.) Проект спас Стив Толбот (Steve Talbott), который настоял на том, что идея книги слишком хороша, чтобы дать ей умереть. Он пригласил Джерри Пика, который незадолго до этого поступил на работу в отдел выпуска как писатель и консультант-разработчик по инструментам UNIX. Отдел проиграл битву, и Джерри присоединился к нам. Он знал больше секретов и приемов UNIX, чем Майк, Дэйл и я вместе взятые. Джерри переработал наш план и провел целый год за написанием и подбором материалов для книги. Я сидел в сторонке и любовался тем, как Джерри придавал моим идеям правильную форму. В конце концов, и Джерри исчерпал себя. Объем книги увеличивался, а он не нанимался писать ее в одиночку! (К тому времени насчитывалось уже около 1000 страниц, а реализована была только половина плана.) Джерри, Майк и я провели неделю, запершись в конференц-зале, уточняя план, читая и урезая параграфы и вообще стараясь дать почувствовать Джерри, что он вовсе не Сизиф. После этого Джерри продолжил работу, но уже не в одиночку, а в команде со мной и Майком. Мы занимались чтением и редактированием. Я особенно благодарен Майку за его активное участие, поскольку у него было много других книг, а эта считалась "моим" проектом. Я не перестаю удивляться глубине познаний Майка и его видению перспективы. К концу проекта Линда Мюи (Linda Mui) завершила работу над другой книгой и присоединилась к нам, занявшись документированием многих бесплатно распространяемых утилит, которые мы не успевали описать. Линда, ты спасла нас на финише! Мы признательны также другим авторам, которые позволили нам использовать (а иногда и критиковать!) их материалы. В частности, выражаем благодарность Брюсу Барнетту (Bruce Barnett) за любезное разрешение использовать многое из написанного им, хотя мы пока не напечатали его книгу, а также Крису Тореку (Chris Torek) за позволение использовать многочисленные находки, публикации о которых он размещал в Сети на протяжении многих лет. Крис не сохранил копий большинства из этих заметок; они были сохранены и присланы читателями Usenet: Дэном Дювалем (Dan Duval), Куртом Дж. Лидлем (Kurt J. Lidl) и Яркко Хиетаниеми (Jarkko Hietaniemi). Предисловие 11
Джонатан Кейменс (Jonathan Kamens) и Том Кристиансен (Tom Christiansen) не только предоставили заметки, но и просмотрели отдельные части книги грамотным и критическим взором. Они предупредили многие наши ляпсусы. Если бы мы могли дать Им достаточно времени на прочтение всего материала, то вряд ли пришлось бы помещать наше стандартное заявление о том, что ответственность за все замеченные ошибки несут авторы. Милтон Пик (Н. Milton Peek) занимался техническим редактированием книги. С отладкой помогли четыре дотошных читателя Usenet: Каспер Дик (Casper Dik) из Амстердамского университета, Байрон Ратзикис (Byron Ratzikis) из корпорации Network Appliance, Дэйв Барр (Dave Barr) из Population Research Institute и Дункан Синклер (Duncan Sinclair). Мы не упомянули многих помощников — людей, на протяжении многих лет размещавших вопросы и ответы в Сети и принявших участие в сплетении изысканного кружева культуры UNIX, которую мы попытались отразить в книге. Джерри выделил одного из главных помощников в своей UNIX-мастерской: "Дэниел Ромике (Daniel Romike) из Tektronix Inc. (который, между прочим, написал параграфы 9.06 и 11.07 еще в начале 80-х) вел первый форум по UNIX, который я посещал. Он находил время, чтобы ответить на мои бесчисленные вопросы, когда я изучал UNIX. Я уверен, что некоторые из находок и тонких приемов, которые придумал я, на самом деле пришли от Дэна". Джеймс Ревелл (James Revell) и Брайан Буус (Bryan Buus) нашли в Сети полезные и интересные бесплатные программы, о существовании которых мы и не подозревали. Кроме того, Брайан скомпилировал большинство собранных им программ, благодаря чему мы смогли испытать их и отобрать нужные. Выражаем искреннюю благодарность всем авторам программных пакетов, которые мы описали ■и поместили на компакт-диск. Если бы не их усилия, нам не о чем было бы писать; если бы не их щедрость, выразившаяся прежде всего в бесплатном предоставлении программ, мы не смогли бы распространять сотни мегабайт программного обеспечения за ту цену, по которой продается эта книга. Джефф Москоу (Jeff Moskow) из компании Ready-to-Run Software оказал нам неоценимую услугу, упаковав программы на диск, перенеся их на основные платформы UNIX и обеспечив беспроблемную инсталляцию. Решение этой проблемы мы отложили на конец,.а оно оказалось более трудоемким, чем мы предполагали. Мы никогда не справились бы с этой задачей, если бы не Джефф и коллектив RTR. Планировалось распространять исходные тексты и двоичные файлы для нескольких платформ, и мы не смогли бы выполнить эту задачу, для каждой существующей платформы без содействия сотрудников RTR. Подготовкой программного обеспечения к копированию на компакт-диск и написанием инструкций по инсталляции мы обязаны Эрику Пирсу (Eric Pearce). Благодарим тебя, Эрик, за активное участие на финише. Ты был прав в отношении того, что многие детали мы могли упустить. Эди Фридман (Edie Freedman) работала над дизайном книги, и благодаря ей мы добились в оформлении издания всего, чего хотели. Она справилась с трудной задачей размещения тысяч перекрестных ссылок, не отвлекающих читателя и не перегружающих текст. То, что она создала, и привлекательно, и удобно в использовании. Нам кажется, что это большое достижение в разработке композиции технической книги! Во всяком случае, мы намерены неоднократно прибегать к этой схеме. Труднейшая задача по реализации всех наших замыслов относительно форматирования текста была поручена также Ленни Мюллнеру (Lenny Muellner), и, надо отметить, справился он с ней достойно. Невозможно не упомянуть замечательного корректора Эйлин Крамер (Eileen Kramer). Даже трудно представить, насколько велик объем выполненной ею работы. Ведь в книге насчитывается более тысячи страниц. Авторы искренне благодарны также Элли Катлер (Ellie Cutler), создателю предметного указателя, Крису Райлли (Chris Reilly), иллюстратору, а также Бонни Хайленд (Bonnie Hyland), Доне Вунтайлер (Dona Woonteiler) и Джейн Апплйард (Jane Appleyard), осуществлявшим дополнительное руководство. — Тим О'Райли
Благодарности ко второму изданию На протяжении 15 лет я самостоятельно изучал UNIX и теперь собираюсь окончить Школу компьютерных наук. Фрэнк Уиллисон (Frank Willison), главный редактор издательства O'Reilly, запланировал разработку этого проекта на лето, приходящееся между моим увольнением из издательства и началом учебы. Тем летом Фрэнк не дал мне отдохнуть, поскольку гонорар помог бы мне оплатить учебу. (Поэтому покупайте книгу и поддержите бедного студента! ;-)) Джиджи Эстабрук (Gigi Estabrook) редактировала настоящее издание и за все это время ответила на миллионы моих вопросов. Большое спасибо Джиджи, Фрэнку и коллективу отдела выпуска компании O'Reilly. Я благодарен Клермари Фишер О'Лири (Clairmarie Fisher O'Leary) и Нэнси Вулф Котари (Nancy Wolfe Kotary), которые совместно выполняли работу редактора выпуска и менеджера проекта, Мэдлин Ньюэлл (Madleine Newell) и Кисмет МакДоно-Чен (Kismet McDonogh-Chan), обеспечившим поддержку выпуска, Шерил Авруч (Sheril Avruch), Николь Гипсон Ариго (Nicole Gipson Arigo) и Дэнни Маркусу (Danny Marcus), следившим за качеством, Ленни Мюллнеру (Lenny Muellner), оказавшему большую помощь в форматировании текста, а также Крису Райлли (Chris Reilly) за чудесные иллюстрации. Я высоко ценю советы, которые давал Арнольд Роббинз (Arnold Robbins), соавтор книги sed & awk (O'Reilly), занимающийся сопровождением GNU-утилиты gawk. Он просмотрел все части книги и предоставил подробные замечания. , Я хотел бы также поблагодарить всех читателей, которые нашли время и прислали комментарии и замечания. Я ознакомился с каждым сообщением. Следует отметить, что идеи читателей значительно повлияли на содержание книги. Замечания трех читателей заслуживают отдельного упоминания. Тэд Тимар (Ted Timar) обратил внимание на проблемы, которые свидетельствуют о его глубоком знании UNIX. Я рад, что он все равно считает книгу полезной для чтения. Эндрю Т. Янг (Andrew T. Young), прислал по электронной почте два длинных сообщения: одно — несколько лет назад, а второе —. после того, как я с ним связался. Он нашел множество технических ошибок и прислал соответствующие поправки. Энди силен не только в вопросах, касающихся UNIX: его большие познания в английском языке помогли исправить несколько грубых стилистических промахов. Наконец, Грег Уббен (Greg Ubben) прислал по электронной почте сообщение на 15 страницах (!), над которым я работал целую неделю. Когда я встретил Грега спустя три года после написания сообщения, он оказал нам еще большую помощь. Из сообщений Грега вполне могла бы получиться небольшая книга, кроме того, он согласился написать несколько параграфов. Грег является специалистом по редактору sed и регулярным выражениям (а также по UNIX), он многому научил меня за месяц переписки по электронной почте. Я высоко ценю его вклад в создание книги. — Джерри Пик, jpeek@jpeek.com Предисловию 13
1 Введение 1,01 Особенности UNIX Если бы мы писали о любой другой операционной системе, термин "инструментальные средства" означал бы "изощренные дополнительные утилиты, расширяющие возможности операционной системы". В отношении UNIX такое определение не совсем корректно: многие утилиты являются неотъемлемым компонентом этой операционной системы (некоторым из них уже более 25 лет). UNIX уникальна тем, что она разрабатывалась не как коммерческая операционная система, предназначенная для выполнения приложений, а как среда для работы системных программистов. Одним словом, данная система создана программистами и для программистов. Кстати, одна из первых версий этой операционной системы появилась под названием PWB (Programmer's Work Bench — рабочее место программиста). Когда Кен Томпсон и Дэннис Ритчи впервые написали UNIX в AT&T Bell Labs, она предназначалась для них самих, а также для друзей и коллег. Утилиты добавлялись разными людьми по мере необходимости. Поскольку компания Bell Labs не занималась компьютерным бизнесом, исходный код предоставлялся университетам за символическую плату. Научные сотрудники самостоятельно писали необходимые программы и добавляли их в UNIX в том стиле творческой анархии, который не исчез до сих пор, за исключением, пожалуй, графической среды X Window (i.3i). В большинстве операционных систем бесплатное программное обеспечение остается без поддержки. В UNIX такая поддержка обеспечивается тысячами независимых программистов. Во время коммерциализации UNIX на протяжении последнего десятилетия процесс подключения внешнего программного обеспечения несколько замедлился, но не приостановился, особенно в университетской среде. Таким образом, книга, посвященная инструментальным средствам UNIX, должна не только освещать надстроечные утилиты, но и обучать использованию расширенных возможностей тех многочисленных утилит, которые в течение многих лет становились частью UNIX. UNIX представляет большую ценность для опытных пользователей, потому что является одной из последних операционных систем, которые не скрываются от пользователя за интерфейсом, состоящим из меню, окон и т.д., и программным интерфейсом, рассчитанным на все случаи жизни (что не всегда соответствует действительности). Конечно, в UNIX можно пользоваться интерфейсом с окнами и меню, и во многих случаях это позволяет сэкономить много времени. Но эта система предоставляет также "строительные блоки", которые позволяют решить гораздо больше задач, чем команды меню оконного интерфейса. Научившись использовать UNIX и ее утилиты, вы сможете выполнять сложные операции с помощью небольшого количества команд, даже не будучи программистом. Данная книга замечательна тем, что обучает некоторым основополагающим принципам, которые делают UNIX похожей на мастерскую умельца. Введение 15
Книга, предлагаемая вашему вниманию, рассчитана главным образом на тех, кто знаком с UNIX на уровне программиста-любителя и стремится стать профессионалом. Однако авторам не хотелось бы оставлять за бортом новичков, поэтому в данной главе раскрыты некоторые основные понятия. В книге вы не найдете полного введения в UNIX — если оно вам действительно необходимо, приобретите пособие для начинающих. Здесь рассмотрены только те ключевые понятия, которые нужно знать, чтобы подняться над уровнем начинающего, а также даны ответы на часто задаваемые вопросы и описаны пути решения типичных проблем. Параграфы в книге расположены несколько произвольно. Авторы не намерены заставлять вас читать главу за главой от корки до корки. Кроме того, мы исключили много вспомогательного материала, который превращает чтение большинства книг в пытку. - TOR, JP Кто воспринимает пользовательский ввод? Мы никогда не обращаемся непосредственно к UNIX. Обращение выполняется к программе, которая называется интерпретатором команд (shell). Эта программа защищает UNIX от пользователя (а пользователя — от UNIX). Основой операционной системы UNIX является ядро (i.U). Обычно к ядру обращаются только программы (через системные вызовы (sj.oo). Пользователи общаются с программой shell. Эта программа интерпретирует их команды и либо выполняет их сама, либо передает другим программам, которые посылают ядру запросы на выполнение операций низкого уровня. Например, в результате ввода команды отображения файлов, имена которых состоят из четырех символов и начинаются с буквы т, и % cat m??? именно shell определит имена файлов, сформирует их полный перечень и вызовет команду cat (25.02) для вывода полного списка. Команда cat поручает ядру поиск на диске каждого файла и выводит его содержимое на экран. Почему это так важно? Во-первых, можно выбирать различные виды интерпретатора shell (Ш), для каждого из которых характерны различные правила интерпретации командной строки. Во-вторых, shell должен интерпретировать набранную пользователем командную строку и преобразовывать в соответствующую команду. Поскольку командную строку первым читает интерпретатор shell, важно знать, каким образом он ее обрабатывает. Например, одним из главных правил является использование в shell пробельных символов (символов пробела и табуляции) для разделения аргументов команды. Но иногда необходимо, чтобы shell интерпретировал аргументы иначе. Например, в некоторых случаях при вызове команды %гер (27.ni) для поиска файлов, содержащих заданную строку текста, необходимо передать в качестве единого аргумента целую фразу. Shell позволит осуществить это, если заключить аргумент в кавычки (8.14). Например: % grep "UNIX Power Tools" articles/* В некоторых случаях — особенно при работе с таким специальным символом о.Ы), как, звездочка (*) в предыдущем примере, — важно понимать, каким образом shell интерпретирует, командную строку. В параграфе 8.05 более подробно объясняется, как shell интерпретирует | введенные пользователем команды. На рис. 1.1 схематически представлены связи между ядром, интерпретатором shell, различными утилитами и приложениями UNIX. Обратите внимание на то, что существует ряд интерактивных программ, принимающих команды непосредственно от пользователя, без вмешательства shell. Текстовый редактор, почтовая программа и почти все приложения (настольные издательские системы, электронные таблицы) содержат собственный интерпретатор команд со своими правилами работы. , - TOR
1.03 Пользователь Приглашение командной строки Пользовательские команды и данные Вывод Интерпретатор Shell Встроенные команды I Передача управления cat Вывод cat II cat Интерактивные команды, управляющие вводом/выводом cat Запросы на обслуживание Ядро UNIX и драйверы устройств ;М-Ш!шШЖи-!> Рис. 1.1. Связи между ядром, интерпретатором shell, утилитами и приложениями Одновременная работа программ Как говорилось в классической книге Брайана Кернигана (Brian W. Kemighan) и Роба Пайка (Rob Pike) The UNIX Programming Environment", существует целый ряд принципов, характеризующих среду UNIX. Один из главных — представление программ как инструментов. Как всякий хороший инструмент, программа должна быть специальной по своим функциям, но применимой для разных целей. Программы не должны зависеть от данных. Для этого следует соблюдать следующие правила: 1. В определенных пределах выходные данные любой программы должны быть применимы как входные данные другой программы. 2. Вся необходимая программе информация должна либо содержаться в потоке передаваемых ей данных, либо определяться в командной строке. Программа не должна запрашивать входные данные или выполнять ненужное форматирование выходных данных. В большинстве случаев это означает, что UNIX-программы работают -с простыми текстовыми файлами, не содержащими непечатаемых или управляющих символов. 3. Если программе не передаются аргументы, она должна читать данные со стандартного ввода (обычно с клавиатуры терминала) и передавать их на стандартный вывод (как правило, на экран терминала). Программы, которые можно использовать описанным способом, часто называются фильтрами. Одним из важнейших следствий соблюдения этих правил является возможность объединения программ в конвейеры (pipelines), в которых выходные данные одной программы используются как входные данные другой программы. Вертикальная черта (|) служит для обозначения канала ам). Этот символ означает следующее: "Взять выходные данные программы слева и передать их программе справа". Например, можно передать по каналу выходные данные программы поиска программе сортировки таких данных, а затем перенаправить полученные результаты программе печати или в файл (i3.ei). — TOR Керкиган Б.В., Пайк P. UNIX — универсальная среда программирования: Пер. с англ. — М.: Финансы и статистика, 1992. — 304 с. 17
Использование каналов для создания нового инструментального средства Тривиальное использование каналов рщ представить несложно. Если о&ьем выходных данных программы велик и они не помещаются на экране, можно передать эти данные программе разбивки на страницы, например more (25.щ. Эта программа выводит экранную страницу данных и ожидает нажатия любой клавиши, после чего отображает следующую страницу. Приведем еще один пример. При написании какого-либо текста (предположим, на английском языке) можно проверить, не следует ли какое-то из слов which заменить словом that. Для этого можно воспользоваться программой поиска grep (27.01), набрав: % grep '[Ww]hioh' chapterl [ mora (Более подробно о каналах — в параграфе 13.1.) Полнее функции каналов раскрываются при решении более сложной задачи. Например, пакет для форматирования текстов troff(43.i3) (в нашем издательстве он используется для подготовки к печати некоторых книг) содержит функцию создания предметного указателя (индексов), которая позволяет пользователю ввести команды индексирования: .XX "тема, подтема" При печати документа пакет форматирования текстов собирает элементы указателя, добавляет номера страниц и формирует предметный указатель. Важно, чтобы элементы предметного указателя были согласованными. Например, если в одном месте текста пользователь введет: .XX "Indexing, introduction to" а в другом: .XX "Index, introduction to" то программа создаст два отдельных пункта, вместо одного со ссылками на две страницы. Проверить согласованность элементов предметного указателя можно с помощью следующей команды: % cat файлы [ grep .XX I sort -u [ more В этой команде аргумент файлы является списком файлов, подлежащих проверке. Команда grep ищет в тексте заданную строку или последовательность.* Команда sort-и (зьму располагает строки, найденные командой grep, в алфавитном порядке и удаляет дубликаты строк. Конвейер начинается с команды cat (25.02), которая выводит содержимое файлов так, что на входе конвейера получается непрерывный поток текста. (Иначе команда grep выводила бы имя файла, в котором найдена заданная строка, что привело бы к неправильной сортировке. В некоторых версиях команды grep можно использовать опцию -И, которая подавляет вывод имен файлов. Чтобы убедиться, что эта опция работает в вашей системе UNIX, наберите grep -h .XX файлы, опустив команду cat и символ канала.) Это весьма скучная работа, но выполнять ее необходимо. И благодаря инструментам UNIX справиться с ней относительно легко. Но... "Фу! — скажете вы. — Именно за это я ненавижу UNIX. Все эти длинные имена файлов и опций невозможно запомнить. Кто захочет набирать всю эту ерунду?!" Да, это правда. Вот почему в UNIX так легко создавать пользовательские команды в виде псевдонимов (Ю.Ю), функций shell (ю.оя) и сценариев shell (i.es). - TOR * Искомая последовательность является регулярным выражением (м.о4), в котором точка служит обозначением любого символа. Предпочтительнее использовать команду grep ' * \. XX'.
1.05 1.05 Программирование в shell Одной из поистине замечательных возможностей интерпретатора shell является то, что он не только читает и выполняет команды, которые вводит пользователь в ответ на приглашение. Язык shell является также полноценным языком программирования. Легкость программирования в shell — настоящий сюрприз для новичков в UNIX. Программа на языке shell может быть не более чем одной сложной командной строкой, сохраненной в файле, или последовательностью команд. Предположим, что вам время от времени необходимо преобразовывать файлы Macintosh Microsoft Word для использования в системе UNIX. Word позволяет сохранить файл в формате ASCII. Но при этом возникает одна проблема: в Macintosh используется символ возврата .каретки (ASCII-символ с кодом 015 (simj) для обозначения конца каждой строки, в то время как в UNIX для этого применяется символ перевода строки (ASCII-код 012). В результате в UNIX файлы выглядят как один бесконечный абзац. Исправить это несложно: команда t^ (35.П) может преобразовать каждый символ возврата каретки в символ перевода строки: % tr '\015' ' \012 ' < ямя_файла. mac > имя_файла. UNIX Вероятно, новичку не захочется запоминать эти магические знаки. Ладно. Сохраните первую часть этой командной строки в файле с именем mac2UNIX и поместите его в подкаталог bin вашего начального каталога (4.юу. tr '\015' '\012' Сделайте этот файл исполняемым при помощи команды chmod (22.07): % chmod +x mac20NIX После этого введите следующую строку: % mac2UNIX < имя_файла. тас > гтя_файла. UNIX Не будем останавливаться на достигнутом. Как преобразовать несколько файлов одновременно? Очень просто. Язык shell позволяет обращаться к аргументам, передаваемым сценарию, а также предоставляет целый ряд конструкций для организации циклов. Сценарий for х do echo "Converting $x" tr '\015' '\012' < "$x" > "tmp.$x" mv " tmp.$x" "$x" done преобразует любое количество файлов при помощи одной команды, заменяя каждый исходный файл преобразованным: % raac2UNIX имя_фа&па1 гтя_файла2 имя_фа6лаЗ ... По мере изучения UNIX становится очевидным, что выполнение ряда подготовительных мероприятий позволит сохранить многие часы, затрачиваемые на рутинные операции. Приведенный сценарий содержит только две программные конструкции: цикл for и подстановку переменньгх (t.os, t.oi). Будучи новичком и не имея опыта программирования, я Изучил эти две конструкции на примере: сохранив заготовку цикла for в файле, я заполнял пробелы теми командами, которые нужно было повторять. Простые shell-программы, подобные приведенной, послужили для меня более вескими аргументами того, что компьютеры могут автоматизировать труд, чем любые другие "открытия" со времени моего знакомства с обработкой текста. Они являются воплощением правила, гласящего: "Поручите компьютеру выполнять грязную работу". Систему UNIX освоить непросто из-за того, что она богата возможностями и сложна. Пользователь, который не желает изучать ее углубленно и знакомиться со всеми нюансами, for 44.I6 Sx 6.0S Аведвято 19
2.06 может не делать этого — основные команды UNIX так же просты, как команды MS-DOS на IBM PC. Однако пользователь, рискнувший потратить время на исследование возможностей системы, может открыть для себя обилие полезных инструментов. - TOR 1.06 Инструменты редактирования Моя жена не позволяет купить мне мощную электропилу: она опасается несчастного случая. Поэтому при осуществлении различных домашних проектов, например, изготавливая полки, я пользуюсь ручной пилой. Однако если бы я зарабатывал на жизнь плотницкими работами, то не обошелся бы без электропилы. Мощность инструментов имеет большое значение для повышения производительности труда, поскольку от нее зависят и скорость работы, и эффективность. Для тех, кто работает с текстами, мощными инструментами редактирования являются программы sed (34.24) и awk (зз.и). Большинство операций, которые позволяют производить эти программы, можно выполнить и в интерактивном режиме при помощи текстового редактора. Однако благодаря программам sed и awk можно сэкономить немало времени, затрачиваемого на выполнение повторяющихся действий. Программы sed и awk сложны и требуют немало времени для изучения, но предоставляемые ими возможности многократно окупают потраченное на обучение время, особенно если редактирование текстов является основным видом вашей деятельности. Обе программы позволяют писать и редактировать сценарии для выполнения операций, которые иначе нужно было бы выполнять вручную при помощи повторяющихся команд редактора типа vi (З0.02). Еще один важный момент: они позволяют производить редактирование данных, передаваемых через каналы UNIX (43.21, is.09. mv, — данных, которые, возможно, никогда не будут записаны в файл. Главным же мотивом изучения sed и awk является их пригодность для конструирования общих решений проблем, возникающих при редактировании текстов. Для большинства людей, и меня в том числе, решение проблемы состоит в превращении тяжелой работы в забаву. При выборе редактора для выполнения последовательности повторяющихся операций редактирования я отдаю предпочтение редактору, sed, а не vi, потому что он делает решение проблем более интересным для меня. Я совершенствую решение, а не повторяю некоторую последовательность действий. Кроме того, по завершении работы я ощущаю себя человеком разумным, совершившим маленькое чудо и избежавшим скучной работы. Поначалу при лсподьзовании программ sed и awk кажется, что на решение задачи тратится много времени. После нескольких попыток возникает впечатление, что проще все сделать вручную. Наберитесь терпения. Вы должны научиться не только работать с sed и awk, но и распознавать ситуации, когда их применение целесообразно. По мере приобретения опыта не только ускорится темп работы, но и расширится круг решаемых вами задач. — DD, из книги sed & awk издательства O'Reilly & Associates 1.07 Возможности системы — в ваших руках Мы отмечали, что UNIX — необычная операционная система. В своей книге The UNIX Programming Environment Керниган и Пайк писали, что в основе философии UNIX лежит следующий принцип: "Мощь системы обеспечивается в большей степени взаимодействием между программами, а не самими программами". v Почти все прикладные программы, работающие под UNIX, используют один и тот же простейший пользовательский интерфейс. Такой интерфейс обеспечивает объединение программ в конвейеры, позволяющие выполнять задания, которые не под силу любой отдельной программе. 20
1.08 Существуют операционные системы, с которыми UNIX не может сравниться, — они обеспечивают более высокую производительность, обладают лучшей документацией и проше в использовании. Однако ни одна из них не будет казаться вам более мощной и приятной в работе, после того как вы постигнете механизм каналов и фильтров, а также широкие возможности языка программирования shell. Начинающие пользователи создают простые конвейеры и, когда они становятся объемными, сохраняют их в файлах (i.os) для дальнейшего использования. Со временем (при условии, что пользователь достаточно настойчив) приходит осознание, что компьютер может выполнять большую часть рутинной работы. Все может начаться с цикла for (9.12), обеспечивающего применение одного и того же сценария для нескольких файлов. Затем появляются условные конструкции, в том числе с оператором case, и очень скоро пользователь начинает чувствовать себя программистом. Большинство систем предполагают целенаправленное изучение языков программирования. А в UNIX ознакомление с приемами программирования происходит исподволь. Этому способствует длительный, непрерывный процесс расширения круга задач, выполняемых во взаимодействии с компьютером. Через какое-то время вы начнете решать нешаблонные задачи, и набора инструментов, предоставленных разработчиками системы, окажется недостаточно. Иногда это называется хакерством, в других случаях — инженерным искусством, А по сути — это способность самостоятельно создать необходимый инструмент, если под руками нет готового. Дэйл Дохерти (Dale Dougherty) сравнил UNIX с "жуком" фирмы "Фольксваген", уникальным автомобилем 60-х и 70-х годов. Именно простота конструкции сделала его популярным — ведь "жука" можно было ремонтировать самостоятельно. Подобное ощущение независимости дает пользователям среда UNIX. В этом отношении она коренным Образом отличается от многочисленных весьма солидных программных, сред, которые держат пользователя на расстоянии, подобно телевизору. Отчасти, секрет UNIX состоит в том, что его рабочие механизмы видны. Среда UNIX, как и автомобиль "Фольксваген", построена так, что пользователи могут снять деталь и установить ее на место. UNIX предоставляет универсальные инструменты, которые можно использовать совместно. Ни одна из программ, как бы хорошо ни была она спланирована, не способна решить всех проблем. Всегда возможны особые случаи, не предусмотренные разработчиком программы. UNIX не является единой программой. Это набор сотен программ. При помощи этих-инструментов грамотный и настойчивый пользователь сможет решить любую вычислительную задачу. Как и-средства любой сложной системы, эти возможности не так просто выявить. Но они ■ доступны. Постепенно вы освоите все возможности и получите систему, которую даже не мечтали иметь. Столкнувшись с возможностью выбора между многократным выполнением вручную скучных операций и наладкой инструмента, который решит задачу мгновенно, многие из нас выберут последний вариант. - TOR 1.08 Виды интерпретаторов shell В большинстве операционных систем интерпретатор команд встроен в систему и является ее неотъемлемой частью. В UNIX интерпретатор команд — это независимая программа. Интерпретатор по"лучил название "shell" (оболочка), возможно, потому, что защищает вас от лежащего ниже ядра (или ядро от вас). Существует несколько видов интерпретаторов shell. Вы можете выбрать тот, который в максимальной степени отвечает вашим интересам или оптимально подходит для данного приложения. Самыми популярными являются интерпретаторы, перечисленные ниже. Введение 21
sh Bourne shell (назван в честь создателя — Стива Бурна) — один из старейших видов. Интерпретатор этого вида присутствует в большинстве систем. В некоторых системах sh заменен интерпретаторами новых видов (например, ksh или bash), которые обладают всеми возможностями sh, а также рядом дополнительных возможностей. Bourne shell несколько примитивен: в нем отсутствуют возможности управления заданиями (возможности переводить задания в фоновый режим). Большинство пользователей UNIX считают Bourne shell идеальным для создания программ или написания командных файлов. csh С shell разработан в университете Беркли как часть их варианта UNIX и приобрел известность как интерпретатор для работы в интерактивном режиме. Интерпретатор данного вида отсутствует в некоторых UNIX-системах версии System V, но это частный случай. С shell обладает многочисленными прекрасными возможностями, отсутствующими в Bourne shell, среди которых — возможности управления заданиями (п.щ и повторения команд, выполнявшихся ранее (перечень ранее введенных команд (n.oij). Однако при создании программ нужно быть очень внимательным, так как можно легко выйти за пределы возможностей С shell (47.02). В интерпретаторе данного вида есть много скрытых ошибок. ksh Kom shell (назван в честь создателя — Дэвида Корна) совместим с Bourne shell, обладает большинством возможностей С shell и рядом совершенно новых функций (к числу последних относится функция редактирования перечня ранее введенных команд (и. v> — повторный вызов прежде выполнявшихся команд и их редактирование перед выполнением). Kom shell более надежен, чем С shell. Кот shell является стандартной частью UNIX System V Release 4, а также включен в ряд других реализаций UNIX. bash "Bourne-again" shell (игра слов: bom again — рожденный заново) разработан организацией Free Software Foundation (53.01). Bash (s.02) очень похож на Kom shell. Он обладает многими возможностями С shell, а также возможностью редактирования перечня и встроенной командой вызова справки. tcsh tcsh (s.os) — расширенная версия С shell. Работает как исходный интерпретатор С shell, но содержит больше возможностей и меньше ошибок. Существует также несколько видов интерпретаторов shell независимых разработчиков, предназначенных для специальных целей, например для эмулирования командного языка VAX/VMS (DCL). Мне неизвестно, существует ли интерпретатор shell, похожий на DOS, но, скорее всего, существует. Другой вопрос, для чего он может понадобиться: любой стандартный интерпретатор shell в UNIX имеет намного больше возможностей, чем интерпретатор команд DOS. Более того, я хотел бы предупредить усилия пользователей UNIX по превращению ее в He-UNIX. Вы собираетесь провести много времени, работая с UNIX, и' лучше изучить возможности этой системы, чем пытаться придать ей вид другой операционной системы. В данной книге предполагается, что для работы в интерактив ном режиме применяются С shell и bash. Поскольку bash и ksh могут выполнять сценарии, написанные для Bourne shell, мы будем использовать sh для создания shell-программ. Информацию о Bourne shell, или sh, можно отнести также к bash и ksh. To же касается С shell, tcsh и, в некоторых случаях, bash. Однако нельзя с уверенностью утверждать, что возможности bash, ksh и tcsh присутствуют в интерпретаторах csh и sh, от которых они произошли. Если вы новичок в UNIX, не старайтесь запомнить названия всех интерпретаторов. В данной книге мы говорим главным образом о С shell и Bourne shell. Для начала этих знаний достаточно. В дальнейшем вы сможете изучить и оценить то, что появилось в ksh, tcsh и bash. - ML, JP Определение вида интерпретатора Определить, к какому семейству принадлежит интерпретатор shell, можно по символу его приглашения. Bourne shell обычно выводит в качестве приглашения символ $. В С shell используется символ %. (Однако в tcsh часто используется символ >.)
1.10 Можно настроить строку приглашения (7.oi) так, чтобы отображалась дополнительная информация. Однако большинство пользователей и системных администраторов придерживаются соглашения о том, что приглашение заканчивается исходным символом. Наберите одну из следующих команд (вторая команда предназначена для систем, использующих NIS (Network Information Service) — информационный сетевой сервис фирмы Sun для работы с сетевыми файлами): % дхер вате_рвгистрационное_имя /etc/passwd % ypcat paaawd | grep вате_рвгистрационное_имя В результате вы должны получить содержимое вашей записи в файле паролей.* Например: tim::23:10:Tim O'Reilly:/home/tim:/bin/csh Поля разделяются двоеточиями. Последнее поле должно содержать название используемого интерпретатора shell. Путевое имя /bin/csh (или /usr/bin/csh) соответствует С shell, а имя /bin/sh — Bourne shell (или Кот shell) и т.д. Пустое последнее поле по умолчанию соответствует Bourne shell. Рассмотрим содержимое остальной части строки. Первое поле содержит ваше регистрационное имя, а второе — зашифрованный пароль, если он задан. Пароль может находиться в файле "теневых паролей". Третье и четвертое поля включают ваш идентификатор пользователя, или UID (38.03), и идентификатор группы, или GID (зя.оз). Пятое поле часто содержит информацию о пользователе, а шестое — имя вашего начального каталога. - TOR 1.10 Внутренние и внешние команды Некоторые набираемые нами команды являются внутренними, т.е. встроенными в shell. Например, к этому разряду относится команда cd. Shell интерпретирует такую команду и изменяет текущий каталог (ui). Команда Is является внешней и хранится в файле' /bin/Is. Для вьтолнения внутренней команды интерпретатор shell не запускает отдельный процесс. Для вьтолнения внешней команды интерпретатор shell должен обратиться к системным вызовам fork и exec (i.ii), чтобы запустить новый подпроцесс (З&оз). На это тратится некоторое время, что особенно ощутимо в перегруженной заданиями системе. (В параграфе 7.04 описана ситуация, когда скорость выполнения команды имеет большое значение.) После того как пользователь ввел команду, интерпретатор shell проверяет, является ли она внутренней, и, если это так, выполняет ее. Если внешняя команда задана полным путевым именем (ui), например /bin/Is, проблем не возникнет: команда выполняется так, как описано выше. Если команда не является внутренней и не указано ее полное путевое имя, shell сначала ищет исполняемую программу или сценарий с заданным именем согласно с последовательностью поиска (алт). Последовательность поиска — это список каталогов, которые интерпретатор shell должен просматривать в поисках файла, имя которого совпадает с именем введенной команды. Последовательность поиска не является встроенной в shell; она создается пользователем и сохраняется в файлах конфигурации shell (2.02). По традиции (21.04) программы хранятся в каталогах с именами /bin и /usr/bin, а дополнительные программы, используемые обычно только системными администраторами, — в каталогах /etc и /usr/etc. Во многих версиях UNIX программы хранятся также в каталоге /usr/ucb (назван в соответствии с наименованием университета в Беркли, штат Калифорния, где написаны многие UNIX-программы). Каталогов, содержащих программы, может быть очень много. Например, программы, из которых состоит X Window (1.31), хранятся в каталоге /usr/bin/Xll. Часто пользовательские командные файлы и сценарии хранятся в каталоге /usr/local/bin. * При выполнении поиска с шаблоном может совпасть несколько записей. При поиске строки tiro можно иайти имя пользователя timothy umfattma. Правильное решение состоит в использовании регулярного выражения (кво " ~ваше_регистрационное_имя:". Введение 23
Последовательность поиска хранится в переменной среды (6.oi) с именем PATH' (6,04). Типичное выражение, задающее значение переменной PATH, может.иметь следующий вид: PATH=/bin:/usr/bin:/usr/bin/Xll:/usr/ucb:/home/tim/bin: Каталоги, приведенные в последовательности поиска, просматриваются по очереди, поэтому, если в них есть командные файлы с одинаковыми именами, будет выполнен тот, который всфетится первым. Во время работы можно добавить в последовательность поиска новые каталоги (я.от>, но обычно последовательность поиска задается в файлах конфигурации shell. - TOR Выполнение внешних команд интерпретатором shell Что происходит, когда shell выполняет внешнюю команду о.rip. UNIX-профаммы выполняются посредством сочетания двух системных вызовов (запросов нижнего уровня к операционной системе) — fork и exec. Системный вызов exec заставляет ядро выполнить внешнюю профамму. При этом ядро заменяет вызывающую профамму новой — той, которая вызывается. Это не очень удобно, если после выполнения вызванной профаммы необходимо вернуться к исходной программе. Данная проблема решается следующим образом. Профаммы, выполнение которых не должно прекратиться, сначала копируют себя при помощи системного вызова fork. Затем скопированная профамма выполняет новую профамму при помощи системного вызова exec и завершается в данном процессе. Вам не обязательно знать подробности того, что происходит.в недрах UNIX, но в некоторых случаях желательно разбираться в особенностях работы команд fork и exec. Дополнительную информацию вы найдете в парафафе .38,02. - TOR Назначение сценариев shell Сценарий shell является ASCII-файлом (що, который содержит последовательность команд. Если вы сохранили список команд в файле для однократного использования, то для выполнения этих команд следует набрать такую строку: % sh мои_конаяды где аргумент мои^_команды является именем файла, содержащего список команд. При вводе этой командной строки интерпретатор shell рассматривает файл как список команд, которые нужно выполнить. ■....■ Существует, лучший способ, извещения интерпретатора shell о том,, что нужна выполнить команды, содержащиеся в файле. Он состоит в преобразовании файла в исполняемый файл при помощи команды chfnod (22.07): % chmod +х мои_комаяда Теперь для выполнения сценария нужно всего лишь набрать имя его файла.. (Чтобы еще больше упростить процесс, сохраните файл в персональном каталоге.bin и добавьте этот каталог в последовательность поиска (ш).) Конечно, в любом случае все строки файла должны иметь смысл для интерпретатора shell. Если вы случайно сделаете исполняемым файлом письмо к матери и попробуете его выполнить как зЬеНтеценарий, то получите сообщения об ошибках, в которых содержатся первые слова каждой строки: letter: Dear: not found Shell не будет пытаться выполнить эти слова как команды, а сообщит, что ему незнакомы команды с подобными именами.
1.14 Кроме того, для эффективного использования сценариев shell нужно знать, как передавать сценарию аргументы (44.is) и как использовать простые программные конструкции а.ю). - TOR 1.13 Почему важно знать основные понятия? Да, мы знаем, что основы, базовые принципы и т.п. очень скучны. Вы покупаете книгу об инструментальных средствах UNIX в надежде прочитать о "крутых" приемах. Безусловно, вам не хочется, чтобы книга начиналась с цикла лекций. Поверьте, эта книга переполнена советами. Но есть веши, которые необходимо знать. Что касается UNIX, основное отличие грамотного пользователя от дилетанта состоит в том, что грамотный пользователь знает, что делает и почему. Дилетант может знать не меньше команд и приемов, но Не иметь представления, когда и зачем применять их. В действительности многие приемы являются не трюками, а очевидными решениями типичных проблем. Наша задача состоит в том, чтобы помочь вам выработать творческий подход к UNIX, подготовить вас к самостоятельному анализу проблем, и. нахождению собственных решений. Принцип "нашел и используй" не. очень хорош, если вы не в состоянии дописать ни одной строки кода. -ML 1.14 Ядро и демоны Те, кто пришел к UNIX из DOS или другой операционной системы, заметят ряд существенных отличий. UNIX была, есть и будет многопользовательской операционной системой. Она остается многопользовательской, даже если вы — ее единственный пользователь. -Это сильно влияет на все, что вы делаете. Почему это так важно? Во-первых, вы никогда не являетесь единственным, кто использует систему, даже если вы так думаете. Не заглядывайте под стол в надежде обнаружить там спрятанный терминал. Его нет. Но UNIX всегда что-то делает "за вашей спиной", выполняя собственные программы, независимо от того, осознаете вы это или нет. Самая важная из этих программ — ядро, сердце операционной системы UNIX. Ядро выделяет для каждой выполняющейся программы память, распределяет время процессора таким образом, чтобы каждая программа могла завершить свое задание, управляет операциями ввода-вывода и т.д. Огромное значение имеют также демоны — помощники системы. Время от "времени они активизируются для выполнения небольших, но важных задач, таких как обработка почты, проведение сетевого обмена, передача данных на принтер, слежение за временем и т.д. '-" ■■'- Можно долгое время работать с UNIX и не подозревать о существовании ядра и демонов. Если вдруг вы увидите на своем терминале сообщение panic и работа системы аварийно прекратится, знайте, что ответственность за это несет ядро. Что-то "смутило" его, и оно . решило отказаться от работы", чтобы не сделать какую-нибудь глупость. Эффективного рецепта борьбы с "паникой" не существует. Кроме того, эта проблема обычно не связана с вашими ошибками. Но нужно знать причину происходящего. В свою очередь демоны могут забросать вас сообщениями. Если вы воспользуетесь командойps (см. ниже), имена некоторых процессов в ее выводе окажутся для вас незнакомыми. Это могут быть имена демонов, которые в данный момент делают что-то полезное. Пока не. стоит, обращать, внимание на. них. Просто ■' нужно знать, что они существуют. Итак, ваш компьютер используется вами, ядром и загадочными демонами. Я сейчас работаю на станции Sun 3. Если дать команду £s (заму, которая выводит список всех выполняющихся программ, можно получить следующий отчет: PID TT STAT TIME COMMAND 1449 со IW 0:01 sunvlew 1453 со S 0:27 clock -Wp 497 32 -Wp 704 0 -Wl -Wh 1 ■.■■* 1451 pO IW '. 0:04 shelltool 145.2 pO IW 0:00 -bin/csh (csh) Вщение 25
1454 pi R 2:52 shelltool 1455 pi S 0:01 -bin/csh (csh) 2217 pi R 0:00 ps 1504 ,p2 S 1:54 shelltool 1505 p2 IW 0:01 -bin/csh (csh) 1884 p2 S 1:32 emacs proncip3.otl Я думал, что работает только мой редактор Emacs, а оказалось, что компьютер делает для меня намного больше. Работает программа sunview, которая управляет монитором, а также выполняется программа, отображающая маленькие часы в углу экрана. Я использую несколько командных средств, которым соответствуют окна (или области экрана), выступающие в качестве отдельных терминалов. Каждая команда применяет интерпретатор shell (csh), который расшифровывает все, что я набираю на клавиатуре. Выполняется также команда ps. И где-то терпеливо ожидает моего ввода одинокий редактор Emacs. В системе X Window (ui) картина иная. Но и в этом случае выполняются минимум две программы, а часто — намного больше. Чтобы получить список всех выполняющихся программ, в том числе и демонов, наберите команду ps -aux (для BSD) или ps -el (для многих других разновидностей UNIX). Вы будете поражены. Результаты работы команды ps заставляют нас по-другому взглянуть на UNIX. Ядро UNIX распоряжается всеми системными ресурсами (временем процессора, памятью, дисками и т.д.). Ядру необходимо запускать не только ваши программы, но также демоны, программы, загружаемые другими пользователями, а также программы, предназначенные для запуска в определенное время (40.oi). При запуске программы ядро выделяет ей небольшой отрезок времени (до секунды) и позволяет работать до тех пор, пока это время не истечет или пока программа сама не решит приостановиться ("заснуть"). В этот момент, независимо от того, завершилось выполнение программы или нет, ядро находит другую программу, которую нужно выполнять. Ядро UNIX никогда не отдыхает. Оно всегда следит за системой. Понимание функций ядра необходимо для понимания работы UNIX в целом. Если вы имели дело с компьютером, то знаете, что его нельзя выключать, когда какая-либо информация записывается на диск, поскольку можно повредить данные на диске и дисковод. То же верно и для UNIX, но при этом возникают также дополнительные сложности. В любое время любая работающая программа может обратиться к диску. Один из демонов считает необходимым обращаться к диску, например, каждые 30 секунд, чтобы отслеживать текущее состояние. Следовательно, недопустимо просто выключить компьютер. При этом можно повредить системные файлы (не только собственные файлы, но и разнообразные файлы, принадлежащие другим пользователям). Чтобы выключить систему UNIX, нужно сначала запустить программу shutdown, которая "отгоняет" всех от системы и дает гарантию того, что какой-нибудь демон не начнет исподтишка "баловаться" с диском. Затем надлежит запустить программу sync, которая обеспечивает завершение всех дисковых операций. Только после этого можно выключить питание. При запуске системы UNIX автоматически запускается программа ,/яЛ (filesystem check — проверка файловой системы). Ее задача — выяснить, правильно ли была остановлена система, и обнаружить повреждения, которые могли возникнуть при неправильном останове. В этой книге мы избегали тем, относящихся к администрированию. Однако программы shutdown, sync и fsck являются хорошим примером того, чем UNIX отличается от более простых операционных систем. Если вы понимаете, для чего нужны такие программы, вы находитесь на пути превращения в грамотного пользователя. - ML Имена фатов Как и во всех операционных системах, в UNIX используются имена: слова (или последователь- ■ ности символов), которые позволяют идентифицировать файл. В ранних версиях UNIX длина i имени файла была ограничена 14 символами. В современных системах это ограничение ] устранено. Конечно, вы можете столкнуться с ограничением, и если это произойдет, то вы излишне словоохотливы.
1.16 Имя файла может содержать любые символы (даже непечатаемые), за исключением косой черты (/). Однако следует избегать применения в именах файлов большинства знаков препинания и непечатаемых символов, поскольку обычно это провоцирует возникновение проблем. При выборе имен для файлов рекомендуем ограничиться следующими символами: • Строчные и прописные буквы. В UNIX регистр всегда имеет значение: система всегда проводит различие между строчными (нижнего регистра) и прописными (верхнего регистра) буквами (в отличие от DOS и VAX/VMS, которые считают строчные и прописные буквы одинаковыми). Следовательно, файлы myflle и Myfile являются разными. Тем не менее, не принято, чтобы имена файлов различались только регистрами символов. • Символ подчеркивания (_)■ Символ подчеркивания удобен для разделения слов, входящих в имя. Например, прочитать мое_длинное_имя_файла легче, чем моедлинноеимяфайла. • Точка (.). В некоторых программах (например, в компиляторе С) посредством точки отделяют имя файла от расширения (i.H). Расширения используются такими программами для определения типа обрабатываемого файла, но не имеют значения для shell, ядра или других UNIX-программ. Имена файлов, которые начинаются с точки, shell обрабатывает особым образом, поэтому шаблоны, содержащие специальные символы (1М), не обеспечат совпадения с такими именами, если не включить точку в шаблон (например, .*). Команда Is, отображающая список файлов, игнорирует файлы, имена которых начинаются с точки, если в нее не включена специальная опция (Is -a (if.ii)). Специальные файлы конфигурации часто делаются скрытыми путем добавления точки перед их именами. • Другие знаки препинания. Единственным знаком препинания, использование которого в именах файлов всегда безопасно, является запятая, хотя она и не входит в POSIX-co- вместимый набор символов. Другие знаки препинания в тех или иных ситуациях могут иметь особый смысл. Избегайте их применения, если не хотите создавать имена файлов, с которыми неудобно работать. Я настолько сильно настроен против использования специальных и непечатаемых символов в именах файлов, что даже не буду рассказывать, как они создаются. Далее мы рассмотрим специальный метод удаления файлов с такими именами (2i.ii). Он пригодится вам, если вы случайно создадите их. Вам необходимо знать следующее: • UNIX не поддерживает концепцию версий файлов. Существуют программы управления версиями (20.12), которые используют собственное понятие версии, но ничего похожего на номер версии в VAX/VMS нет. При редактировании файлов не рассчитывайте, что UNIX сохранит предыдущие версии, хотя для этого можно создать сценарии (41.и). Редактор GNU Emacs также образует резервные копии (32.04). • В UNIX после удаления файл исчезает окончательно рз.Ю). Его невозможно восстановить никаким образом, кроме как на основе резервной копии. Будьте внимательны при удалении файлов. Ниже мы расскажем о программах (23.es, 23.09), разграничивающих ао времени удаление файла пользователем и его фактическое исчезновение. - ML 116 Специальные символы shell Shell поддерживает множество специальных символов, которые можно использовать для сокращения имен файлов и обращения к группе файлов. Допустим, в текущем каталоге (uo нужно удалить все файлы, имена которых заканчиваются символами .txt. Можно удалять файлы один за другим, что нецелесообразно, особенно если файлов очень много. Более эффективный способ — применение специального символа в обозначении имени файла, удаление которого можно описать следующей фразой: "Я хочу удалить все файлы, имена которых заканчиваются символами .Ш, независимо от того, какая у них первая часть имени". Специальный символ служит для обозначения неизвестной части имени файла. Он может соответствовать любому символу. вЩвние 27
1.16 . Наиболее часто в качестве специального символа употребляется звездочка (■*), но мы сначала рассмотрим более, простой символ — знак вопроса (?). Знак вопроса в имени файла соответствует одному символу. Например, обозначение letter? соответствует любому имени файла, которое начинается символами letter и содержит после них еще один символ. Это могут быть имена letterA, letterl, а также имена со специальным символом вместо последней буквы, например letter^C. Специальный символ * соответствует любому символу, группе символов или ни одному символу. Например, обозначение *. txt соответствует всем именам файлов, которые завершаются символами .txt, а обозначение *.с — всем именам с окончанием .с (по соглашению — исходные тексты программ на языке С). Хотя в большинстве случаев символов * и ? достаточно, некоторые задачи не могут быть решены с их помощью. Предположим, необходимо перечислить файлы, имена которых заканчиваются символами .txt, mail или let. Сделать это при помощи знака * невозможно: он не позволит исключить нежелательные файлы. В подобном случае нужно использовать отдельные знаки * для каждого окончания имени: *.txt *mail *let Иногда требуется создать шаблон, соответствующий одному из символов группы. Предположим, необходимо выполнить поиск файлов с именами program.n, где .п — номер, состоящий из одной цифры. В таком случае используйте следующий шаблон: program.[0123456789] Таким образом, специальные символы типа [список_симво.гов]' могут замещать любой одиночный символ из списка. В список могут входить произвольные группы ASCII-символов. Если они образуют последовательность (например, A-Z, a-z, 0-9, 3-5), допускается применение дефиса. Например, список [a-zA-Z] соответствует любой букве алфавита. Существует одно исключение из правил работы со специальными символами. С их помощью нельзя задать символ /, который является именем корневого каталога файловой системы ц.щ, а также разделителем имен каталогов в -путевом имени (i.2i)'. Если раньше вы не работали с компьютерами, то освоите специальные символы UNIX очень скоро, а если работали с другой операционной системой; то нужно иметь в виду одну существенную деталь. Почти все ОС рассматривают точку как специальный символ в имени файла. Многие операционные системы даже требуют присутствия точки в имени файла. В таких системах * не соответствует точке, в связи с чем требуется вводить *. *. Следовательно, в большинстве операционных систем. значение записи типа гт * невелико. А вот в UNIX это означает "удалить все файлы в текущем каталоге, независимо от их имен". Однако, существует исключение из исключения. Shell и команда Is рассматривают точку как специальный символ, если она является, первым символом имени. Такие имена часто используются для сокрытия файлов инициализации и других файлов, с которыми мы обычно не работаем'. Команда &• не выводит такие имена файлов, если не попросить (i6.ii) ее об этом. Если имя файла начинается с точки, ее всегда нужно указывать явно. Например,: обозначение .^гс соответствует всём файлам, имена которых начинаются с точки и заканчиваются символами гс. Это является общепринятым соглашением по наименованию файлов инициализации в UNIX. В табл. 1.1 описаны все разновидности специальных символов shell. Таблица 1.1. Специальные символы shell Специальный символ ? * [ab] [a-z] . Соответствие Любой одиночный символ Один символ, несколько символов или ни одного символа Символ а или b Любой символ от а до z Специальные символы можно использовать в любом месте или в нескольких местах путевого j имени. Помните, что комбинации со специальными символами ставятся в соответствие только | ; : -. 1^ - ■ - ■ : - ZZ 1 " ч 28 - ■■■'*
1.17 существующим именам. Их невозможно использовать для создания новых файлов (ощ, хотя некоторые интерпретаторы shell применяют для этого фигурные скобки ({}) (v.os, нщ. Об интерпретации специальных символов рассказывается в параграфе 1.18. - ML Расширения имей фатов В DOS и некоторых других файловых системах имена файлов часто имеют структуру имя.расширение. Например, для файлов Lotus 1-2-3 характерно расширение .wkl. Такие операционные системы рассматривают расширение независимо от имени файла, накладывают определенные ограничения на длину имен и т.д. В UNIX нет четких правил относительно расширений имен файлов. Точка не имеет специального значения и не рассматривается как разделитель, а расширение может быть любой длины. Однако целый ряд программ (особенно компиляторы (52.щ) все-таки используют расширение, состоящее, как правило, из одного символа, для распознавания различных типов файлов, с которыми они работают. Кроме того, существует целый ряд соглашений, принятых пользователями и помогающих определять содержимое файлов. (Например, текстовый файл, содержащий замечания по проекту, можно назвать notes.txt.) В табл. 1.2 перечислены некоторые расширения имен файлов и кратко описаны программы, которые их распознают. Таблица 1.2. Расширения имен файлов, используемые некоторыми программами Расширение .а .с / .F ■gz .h Jitml .0 .s ■Z Z .1 - .8 Описание Архивный файл (библиотека) Исходный текст на языке С (S2.ot) Исходный текст на языке FORTRAN Исходный текст на языке FORTRAN для препроцессора Файл, сжатый программой gzip (24.97) Файлы заголовков на языке С (52.08) HTML-файлы для Web-серверов Объектный файл (52.08) Код на языке ассемблера Упакованный файл Сжатый файл (24.07) Исходный файл диалогового руководства (so.bd В табл. 1.3 перечислены расширения файлов, которые часто применяются пользователями для обозначения содержимого файла, но не распознаются самими программами. Таблица 13. Расширения Расширение .txt .tar .shar .sh .csh .mm .ms ■PS имен файлов, используемые для обозначения содержимого файла Описание Текстовый ASCII-файл Архив, созданный программой tar (i9.os) Архив, созданный интерпретатором shell (i9.<>2) Сценарий Bourne shell (l.os) Сценарий С shell (47.02) Текстовый файл, содержащий макросы mm (4з.Ы) для программы troff Текстовый файл, содержащий макросы ms (43.14) для программы troff Исходный текст на языке PostScript - ML, TOR 29
1.18 1.18 Обработка специальных символов Специальные символы (i.w в действительности определяются интерпретатором shell, а не файловой системой UNIX. Теоретически новый интерпретатор shell может зарезервировать новые специальные символы. На практике все интерпретаторы shell в UNIX (в том числе ksh, bash и интерпретаторы других видов (i.os)) следуют одним и тем же соглашениям о специальных символах, и каких-то изменений в правилах не предвидится. (Однако, когда специальные символы не обеспечивают совпадения (is.o4), интерпретаторы реагируют по-разному.) В специальном интерпретаторе shell, эмулирующем другую операционную систему (например, в shell, который ведет себя как система DCL фирмы DEC), могут использоваться другие наборы специальных символов. Тогда интерпретатор shell будет следовать правилам, принятым в соответствующей операционной системе. Но и в этом случае разработчики операционной системы наверняка придерживались общепринятых соглашений. Тот факт, что специальные символы задает shell, а не файловая система или выполняющаяся программа, имеет важные следствия для некоторых команд. Чаше всего программа никогда не обрабатывает специальных символов. Например, команда % lpr * аналогична команде % lpr файл1 файл2 файлЗ файл4 файл5 В этом случае все выполняется так, как и ожидалось. Но бывают ситуации, в которых специальные символы не работают вообше. Предположим, необходимо прочитать некоторые файлы с ленты, для чего требуется команда tar х (2о.о4). Для этого вы набираете tar х * . txt. Будет ли выполнение этой команды успешным? Нет, поскольку shell выпдлняет подстановку в записи *. txt в соответствии с содержимым текущего каталога (Ш), прежде чем передать полную командную строку для выполнения командой tar. Команда tar всегда получает список файлов. Но вас, возможно, не интересует содержимое текущего каталога, а символ * должен быть заменен именами файлов с расширением .txt, содержащихся на ленте. Существует способ передачи специальных символов программе без их интерпретации в shell. Достаточно заключить *.txt в кавычки (8.14). Кавычки предотвращают раскрытие специального символа интерпретатором shell, и он передается команде без изменения. Программы, которые можно использовать таким образом (например, шср и гср (из/), знают, как обрабатывать специальные символы, соблюдая те же правила, что и в shell (на самом деле эти программы запускают shell для интерпретации своих аргументов). Нужно только обеспечить передачу специальных символов программам и проследить, чтобы интерпретатор shell не обработал их перед передачей аргументов команде. Следует четко представлять, когда и как специальные символы обрабатываются интерпретатором и каким образом обеспечить выполнение этой операции в нужный момент. Примечание: Если ваш интерпретатор shell воспринимает символы {) (9.05), то их можно использовать для генерации любой строки, а не только имен существующих файлов. Нужно набрать уникальную часть каждого имени, а общую часть — только один раз. Например, чтобы . прочитать с ленты файлы с именами project/wk9/summary, project/wkl4/summary и pro- ject/wkl5/summaiy, надлежит использовать следующую команду tar: % tar xv project/wk{9,14,15}/sununary х project/wk9/summary, 3161 bytes, 7 tape blocks x project/wkl4/summary, 878 bytes, 2 tape blocks x project/wkl5/summary, 2268 bytes, 5 tape blocks : Некоторые версии команды tar понимают специальные символы, но многие не понимают, я Существуют методы преодоления этой проблемы (2ом). | -ML 1 30
1.19 1.19 Структура файловой системы В многопользовательской системе должна быть предусмотрена возможность формирования пользователями разных файлов с одинаковыми именами. Необходим также механизм объединения файлов в логические группы. При наличии тысяч системных файлов и сотен пользовательских файлов довольно опасно содержать все файлы в одном месте. Даже в однопользовательской операционной системе считается необходимым отойти от одноранговой структуры файловой системы. Почти во всех операционных системах файловая система имеет древовидную, иерархическую, структуру. И UNIX в этом отношении не является исключением. Чтобы понять суть иерархической файловой системы, сделаем одно сравнение. Систематизация файлов осуществляется по тем же принципам, что и систематизация документов в офисе. Ряд шкафов в офисе состоит из отдельных шкафов. Каждый шкаф имеет несколько ящиков, а каждый ящик состоит из нескольких секций. В каждой секции находится несколько папок, а в каждой папке хранится несколько документов (файлов). Найти конкретный документ можно, назвав шкаф, ящик, секцию, группу папок и отдельную папку. Можно, например, отдать такое распоряжение: «Принесите мне документ "Совещание от 9-го июля" из папки "Кайзер" в секции "Планы медицинского страхования" ящика "Соцстрах" в шкафу "Персонал"». Такой путь имеется у каждого документа, хранящегося в шкафу (рис. 1.2). Это позволяет однозначно определить местоположение документа. По такому же принципу, но в обратном порядке, описывается путь доступа к файлу (в имени файла сначала указывается общая часть). Рис. 1.2. Иерархическая система хранения документов Файловая система UNIX функционирует таким же образом (как и большинство других иерархических файловых систем). Чтобы не образовалось множество беспорядочно расположенных разнородных файлов, они распределяются по каталогам. Каталог — это не что иное, как файл специального вида, в котором перечислены другие файлы (см. параграф 18.02). Каталог может содержать любое количество файлов (хотя, с точки зрения быстродействия, количество файлов в одном каталоге не должно превышать 100). В состав каталога могут входить другие каталоги. Поскольку каталог является специальным файлом, он имеет имя. На вершине ("дерево" файловой системы перевернуто) находится каталог, который ЙЩениё 31
называется корневым и имеет специальное имя — / (произносится "слэш", но никогда не записывается в виде слова). Чтобы найти файл, можно использовать цепочку имен (начиная с корневого каталога файловой системы), указывающую его точное местоположение в файловой системе: мы начинаем с корневого каталога и перечисляем каталоги, через которые нужно пройти, чтобы найти файл, разделяя имена каталогов косой чертой. Такая запись называется полным путевым именем. Рассмотрим простую файловую систему, представленную на рис. 1.3. Путевые имена /home/mkl/mystuff/stuff и /home/hun/publick/stuff относятся к файлам с именем stuff Это разные файлы, поскольку они находятся в разных каталогах. Имена home, hun и т.п. являются именами каталогов. Полные путевые имена, подобные приведенным, иногда называют "абсолютными путями". Более простой способ поиска файла состоит в указании относительного ПУТИ (1.21). - ML I |; (Корневой каталог) „,55 ■ -ШШ-Ш'-М 1 \ local ! V.-.W L -„ "Г" t S 1 1 bin | ucb home etc hun Щ other Ш mystuffj: | private hisstuff publick work t; -Каталог stuff У -Файл ( stuff j§ Г stuff \ Рис: 1.3. Дерево файловой системы UNIX Ваш начальный каталог DOS и Macintosh имеют иерархические файловые системы (i.i9), у которых много общего с файловыми системами UNIX и других больших операционных систем. Однако между ними есть и существенная разница. Во многих системах DOS и Macintosh пользователь начинает работать с "корня" файлового дерева: создает подкаталоги, чтобы упорядочить свои файлы. В UNIX "точкой отсчета" является уже готовое чрезвычайно большое дерево файловой системы. При входе в систему вы оказываетесь где-то в нижней части дерева, в каталоге, созданном для вас системным администратором (которым можете быть вы сами, если вы администрируете свою систему). Этот каталог, т.е. участок файловой системы, который принадлежит лично вам и в котором хранятся ваши файлы (в первую очередь файлы конфигурации shell (2.02), используемые для настройки остальной части среды), называется начальным каталогом. Раньше начальные
ш каталоги входили в состав каталога /usr (в некоторых системах они до сих пор там хранятся). Теперь в большинстве случаев они содержатся в таких каталогах, как /и и /home. Чтобы перейти из текущего каталога (и» в начальный, выполните команду cd, не указывая никакого пути. В этом случае интерпретатор shell полагает, что вы имеете в виду свой начальный каталог. В параграфе 14.11 рассматриваются "уменьшительные имена" начальных каталогов. - TOR 1.21 Создание путевого имени Путевое имя позволяет определить местоположение файла (каталога или любого другого объекта) в файловой системе UNIX. На рис. 1.4 представлена небольшая часть файловой системы. Рис. -1.4. Ч?сть дерева файловой системы UNIX Тот каталог, в котором вы работаете, называется текущим. По умолчанию UNIX ищет указанные пользователем файлы или каталоги в пределах текущего каталога. Таким образом, если не указано полное путевое имя (начиная с корневого каталога, /), UNIX осуществляет поиск файлов относительно текущего каталога. Сразу после входа в систему текущим является ваш начальный каталог (ио), который назначен системным, администратором. Обычно начальный каталог имеет имя такого вида: /usr/mike или /home/mike. Изменить текущий каталог позволяет команда cd, к которой добавлено имя нового каталога (например, cd /usr/bin). Определить имя текущего каталога можно посредством команды pwd (print working directory — вывести имя рабочего каталога). Если текущим является каталог /home/mike и вы ввели команду cat torf/?fe, UNIX будет искать файл textflle в каталоге /home/mike. Если ввести команду cat notes/textfile,. будет произведен поиск файла textflle в каталоге notes внутри каталога /home/mike. Существует целый ряд сокращений, с помощью которых очень удобно формировать относительные путевые имена. Так, точку можно использовать для создания ссылки на текущий рабочий каталог, а две точки — для создания ссылки на родительский каталог текущего каталога. Например, если текущим является каталог /home/mike, то записи ./textflle, textflle и /home/mike/textflle эквивалентны. Относительный путь ../gina/textflle соответствует ЙЩеиие 33 2 9-171
абсолютному пути /home/gtna/textfile. Две точки обеспечивают переход по дереву каталогов на один уровень вверх относительно каталога /home/mike (в каталог /home) и поиск файла textfile в каталоге gina. В С shell, ksh и bash можно использовать сокращение ~ (тильду) для создания ссылки на начальный каталог. Запись -name является обозначением начального каталога пользователя с именем пате (см. также параграф 14.11). Ниже приведены правила интерпретации путевых имен, принятые в UNIX: • Если путевое имя начинается с /, это абсолютный путь от корневого каталога. • Если путевое имя начинается с ~ или ~пате, csh, ksh и bash преобразуют тильду в полное путевое имя вашего начального каталога (~) или каталога пользователя пате (~пате). • Если путевое имя не начинается с /, путь задается относительно текущего каталога. Относительно каждого каталога можно создать путевые имена следующих двух видов: 1. Если путевое имя начинается символами ./, то путь начинается с текущего каталога: ./textfile. 2. Если путевое имя начинается символами ../, то путь начинается с родительского каталога. Например, если текущим является каталог /home/mike/work, то записи ../six соответствует путевое имя /home/mike/src. В параграфе 18.02 объясняется происхождение сокращений . и .. . Примечание: Сокращения . и .. означают: "текущий каталог в данном месте путевого имени" и "родительский каталог в данном месте путевого имени". Если путевое имя начинается символами ../../ (или еще большим количеством символов ../), то путь начинается с каталогов, которые расположены на два и более уровня выше текущего каталога. Эти символы могут находиться в любом месте путевого имени. Например, /usr/ucb/./bin — то же, что и /usr/ucb/bin, a /usr/ucb/bin/./lib — то же, что и /usr/ucb/lib. Применение сокращения . или .. в середине путевого имени может быть полезным при указании пути в сценариях shell. Других примеров мы не знаем. - ML, JP Как UNIX следит за файлами: индексные дескрипторы Способность болтать об индексных дескрипторах является составляющей успеха в компании специалистов по UNIX. И каким бы скучным это вам не казалось, рано или поздно нужно разобраться, что же это такое. Индексные дескрипторы являются важной частью файловой системы UNIX. Хотя в большинстве случаев информация о дескрипторах не нужна, вам не помешает иметь представление' о них. Индексный дескриптор — это структура данных на диске, описывающая файл. В ней хранится 1 большая часть важной информации о файле, в том числе дисковый адрес его блоков данных ; (т.е. той части файла, которая, собственно, нас интересует). Каждый дескриптор имеет свой, номер (i-number). Где именно расположен файл на диске, вам знать не нужно. Обычно мы не догадываемся о существовании номера индексного дескриптора, пока не попытаемся найти ссылки (ism. I7.22) на файл. Большое значение имеет следующая информация, хранящаяся в индексном дескрипторе: • Принадлежность файла — пользователь и группа (22.13), которые владеют файлом.
123 • Режим доступа к файлу о.гз, 22.02) — разрешения, которые даны пользователям и пользовательским группам (разрешения на чтение, изменение или запуск файла). • Временные характеристики файла pt.os, 21.06) — даты последнего изменения файла, последнего сеанса доступа к нему и последнего изменения индексного дескриптора. • Тип файла — является ли файл обычным, специальным или другим объектом, замаскированным (1.29) под файл. Каждая файловая система имеет фиксированное количество индексных дескрипторов, которые образуются при создании файловой системы (обычно при начальной инициализации диска). Таким образом, это количество является максимальным количеством файлов, которые может содержать файловая система. Оно не может быть изменено без повторной инициализации файловой системы, которая сопровождается разрушением всех данных, хранящихся в файлах. Файловая система может (хотя и редко) испытывать нехватку индексных дескрипторов, но это случается только при наличии множества файлов небольшого объема. Команда Is -I (22.02) выдает большую часть информации из индексного дескриптора. Команда \s_ -± (18.04) выводит номер индексного дескриптора. Команда sta[ (21.13) предостаатяет практически всю информацию, содержащуюся в индексном дескрипторе. - ML 123 Права доступа к файлу В UNIX права доступа к файлам устанавливаются как для отдельных пользователей, так и для групп. Для каждого пользователя системы создается уникальная учетная запись с уникальным регистрационным именем и идентификатором пользователя (UTD) (js.oj). Предусмотрена возможность создания учетных записей для групп пользователей. Например, в приложении для обработки транзакций может быть присвоено единое регистрационное имя всем сотрудникам, которым разрешен доступ к нему (UNIX воспринимает эту группу людей как одного пользователя). В некоторых организациях определенные административные операции могут быть упрощены, если наряду с личными учетными записями членов коллектива существует общая учетная запись. Однако в большинстве случаев каждый пользователь системы имеет только один идентификатор. Каждый пользователь может быть членом одной или нескольких групп.* Запись пользователя в файле паролей (/etc/passwd (Зб.оз)) определяет его первичное членство в группе. Перечень существующих групп хранится в файле /etc/group (22.13). Здесь вы можете включить пользователя в ту или иную группу. Например, я являюсь членом трех групп: staff, editors и research. Моя первичная группа — staff В файле group указано, что я также являюсь членом групп editors и research. Группы editors и research — мои вторичные группы. Файлы passwd и group ведет системный администратор. Каждый файл принадлежит одному пользователю или группе. Владельцем файла является пользователь, который создал его. Группой данного файла является первичная группа владельца или группа, владеющая каталогом, в котором создан файл (22м, 22. и). Например, все файлы, созданные мной, принадлежат пользователю mikel и группе staff Как владелец я имею право использовать команду chgrp для изменения группы, к которой принадлежит файл. В файловых системах, не имеющих квот (24.17), я также вправе использовать команду chown для изменения владельца файла. (Об изменении владельца в файловых системах с квотами говорится в параграфе 22.21.) Например, для изменения файла data таким образом, чтобы его владельцами стали пользователь george и группа others, нужно выполнить следующие команды: % chgrp others data % chown george data \ * В версии UNIX, разработанной в Беркли, и в некоторых более новых версиях пользователи имеют все привилегии доступа, определяемые группами, к которым они принадлежат. В других системах UNIX для изменения группы, к которой принадлежит пользователь, используется команда типа newgroup. введемте 2» 35
Если изменению подлежат группа и пользователь, в первую очередь нужно изменять группу. Ведь вы лишитесь права на изменение группы, к которой принадлежит файл, после того как перестанете быть его владельцем. Некоторые версии команды chown позволяют изменять пользователя и группу одновременно: % chown george.others data GNU-версии команд chown и chg/р можно найти на компакт-диске. Доступ к файлу осуществляется с учетом его владельца и группы, а также набора бит, задающих права доступа (биты режима). При попытке доступа к файлу пользователь относится к одной из трех категорий. Он является либо владельцем файла, либо членом группы, либо кем-то другим. Для каждой категории пользователей отводится по три бита (рис 1.5), которые определяют, имеете ли вы право читать, модифицировать или запускать на выполнение данный файл. Выполнение (владелец) Запись (владелец) Чтение (владелец) 1 Г Чтение (другие) Запись (другие) Выполнение (другие) Чтение (группа) ■ Запись (группа) ■ -t t t - Выполнение (группе) Рис. 1.5. Назначение битов режима Обычно девять битов режима представляются как три восьмеричные цифры, причем каждая цифра определяет права доступа для одной из категорий пользователей. Одна восьмеричная цифра соответствует трем битам. На рис. 1.6 продемонстрирована замена битов режима восьмеричным- числом. Преобразуем биты режима 111101001 в восьмеричное число. Разобьем биты режима на три группы: 111 101 001. Первая цифра равна 4+2+1=7. Вторая — 4+0+1=5. Третья — 0+0+1=1. Следовательно, биты режима 111101001 можно представить восьмеричным числом. 751. Еще раз изучите рис. 1.5 и попробуйте выполнить операцию преобразования самостоятельно. Например, если владелец файла имеет право на чтение и запись, а остальным доступ к файлу запрещен, режим доступа файла — 600. Файл, доступный всем для чтения, записи и выполнения, имеет режим доступа 777. Файл, доступный всем для чтения и изменения (например, общедоступный текстовый файл), имеет режим доступа 666. Принято изображать биты режима в виде цепочки из десяти символов (посмотрите вывод команды Is -I (22.02)). Первый символ обозначает тип файла. Обычному файлу соответствует символ -, каталогу — символ d. Следующие три символа служат для обозначения прав Доступа владельца, следующие три — прав группы, а последние три — прав других пользователей. Буква г означает право на чтение, w — право на запись, ах — право на выполнение. Например: -rw соответствует режиму доступа 600 -rwxrwxrwx соответетвует режиму доступа 777 -rw-rw-rw- соответствует режиму доступа 666 Строку rw-rw-rw- можно преобразовать в восьмеричное число при помощи методики, показанной на рис. 1.6. Разделите строку на три группы битов. Например, фрагменту rw- соответствует значение 4+2+0=6. Следовательно, строке rw-rw-rw- соответствует восьмеричное число 666.
из Если первый бит равен 1, ' прибавьте к восьмеричному числу 4 г Биты режима шлЩ Ш;||;| щк Если второй бит равен 1, прибавьте к восьмеричному числу 2 Если третий бит равен 1, прибавьте к восьмеричному числу 1 }№ШЧ%!%«т№<!^ режима = J 11 '^НРШы^ $Щ§ О ■ ■ О '■■ ■ ■ |':'. Воамеричное чиыто = 751 -- . 4+2+1=7 Рис. 1.6. Замена битов режима восьмеричным числом Если файл является исполняемым, приобретают значение некоторые дополнительные биты. Один да них :— sticky-бит, сообщающий UNK о том, что после вьтолнения файл нужно оставить в памяти. Теоретически это ускоряет запуск программы следующими пользователями. В свое время введение sticky-бита было интересной идеей, но сегодня он является излишеством: современные технологии виртуальной памяти, например подкачка по запросу, сделали его ненужным. Многие пользователи UNIX все еще считают, что sticky-бит выполняет какую-то важную роль, поэтому время от времени о нем можно услышать. Более важными являются биты SUID (set user ID — смена идентификатора пользователя) и SGID (set group ID — смена идентификатора группы). Если вы запускаете на выполнение программу с установленным битом SUID, то она приобретает права владельца файла. Если программа с таким битом принадлежит пользователю root, то на время вьтолнения она приобретает права суперпользователя. Запуск программы с установленным битом SGID приводит к тому, что на время выполнения она приобретает права той группы, к которой принадлежит файл. Биты SUID и SGID не обеспечивают полной безопасности, но повышают защиту файлов от нежелательного доступа. Предположим, вы хотите дать каждому пользователю разрешение на создание резервной копии на Ленте, но при этом не желаете предоставлять всем пароль суперпользователя. Можно создать специальную версию утилиты dump, которая принадлежит пользователю root и имеет установленный бит SUID. Когда пользователь вызовет эту утилиту, он сможет создать резервную копию всей файловой системы, поскольку команда dump будет выполняться так, как будто ее запустил пользователь root. Но этим возможности пользователя ограничатся: он не знает пароля суперпользователя и не сможет сделать то, что запрещает ему утилита dump. При грамотном использовании бит SUID превращается в мощный инструмент администратора.* ' В восьмеричной форме права доступа к файлам с установленными битами SUID и SGID записываются : соответственно с цифрами 4 и 2 в первой позиции (4755, 2755, ...). В символьном виде установка этих битов обозначается буквой s для соответствующей категории (r-sr-xr-x, rwxr-sr-x, ...). — Примеч. ред. 37
1.24 Примечание: Программы с битами SUID и SGID не защищены в полной мере от несанкционированного доступа, поэтому многие добросовестные администраторы отказываются добавлять новые SUID-утилиты. Некоторые версии UNIX игнорируют биты SUID и SGID для shell-сценариев (командных файлов). В этих версиях только скомпилированные программы могут иметь биты SUID и SGID. Программы с битами SUID и SGID всегда теряют свои особые свойства при копировании. Обеспечить полную защиту таких программ очень трудно (а может, и невозможно). Хорошо это или плохо, но многие стандартные утилиты UNIX • (например, uucp и Ipr) являются SUID-утилитами. В параграфе 22.0L вы найдете дополнительную информацию о правах доступа к файлам. - ML 1.24 Суперпользователь (пользователь root) Выполняющуюся программу обычно называют процессом (заяц; это может быть shell, команда Is, редактор vi и т.д. Чтобы уничтожить процесс (за.ю), изменить его приоритет (З9.щ или выполнить по отношению к нему какое-либо другое действие, нужно иметь статус владельца процесса (т.е. пользователя, который запустил его). Например, удалить задание из очереди печати (43.oi) может только тот пользователь, который создал это задание. Как вы, наверное, догадываетесь, должен быть способ обхода всех средств защиты. Кто-то должен иметь возможность прекращать выполнение программы, изменять системные файлы и т.д. В UNIX такими привилегиями обладает пользователь root (суперпользователь), который имеет право делать все, что угодно. В любой системе пользователь root обязательно имеет пароль. Системный администратор должен быть очень внимательным при выборе пароля суперполъзователя, и его не следует осуждать за то, что он не хочет раскрывать этот пароль всем подряд. Системы UNIX всегда отличались демократичностью: во многих случаях все пользователи знали пароль суперпользователя и не стеснялись пользоваться им при появлении малейших проблем. То, что общепринято, не всегда хорошо — системы, в которых пароль суперпользователя известен каждому, не обладают надежными средствами защиты. Пользователи могут читать,- чужую почту, просматривать все файлы, записывать на диск всякую ерунду, отправлять конкуренту секретную информацию компании, а также удалять файлы регистрации без возможности их восстановления. Более того, даже если все пользователи — ангелы, суперпользователь случайно может стать виновником крупных неприятностей, ошибочно набрав, например, команду удаления всех файлов rm * в каком-то важном каталоге. Мудрые : системные администраторы никогда не используют статус суперпользователя без острой ■ НеОбХОДИМОСТИ (22.22). 5 При написании книги авторы исходили из того, что пароль суперпользователя вам неизвестен. -\ Почти все, о чем мы рассказываем, можно сделать, не будучи суперпользователем. \ -ML \ \ 1.25 Доступ к каталогам | В UNIX для доступа к каталогам применяются те же биты режима (1.23), что и для доступа к файлам, но интерпретируются они иначе. Способ интерпретации битов станет понятным, если вспомнить, что каталоги являются не чем иным, как списком файлов. При создании файла в каталоге, а также при переименовании или удалении файла необходимо соответствующим образом изменить этот список. Следовательно, чтобы создавать или удалять файлы, пользователь должен иметь право на запись в каталог. Изменение содержимого файла не предполагает внесения изменений в каталог, поэтому изменять файл можно, даже не имея права на запись в каталог! (при условии, что вам предоставлено право на запись в файл). 38
1.27 Процедура чтения каталога относительно проста: пользователь должен обладать правом на чтение каталога, чтобы иметь возможность получения списка содержащихся в нем файлов. Если вы не имеете права на чтение каталога, то не сможете определить его содержимое. Однако (как это ни удивительно) вы все равно сможете обращаться к файлам каталога, если вам известны их имена. Право на выполнение по отношению к каталогу лишено смысла, поэтому разработчики UNTX изменили значение соответствующего бита. Он называется битом поиска. Право на поиск необходимо для проведения операций с каталогами и подкаталогами, например, для входа в каталог. Другими словами, если вы не имеете права на выполнение для данного каталога, то вам не разрешен доступ к этому каталогу и ко всем его подкаталогам. Бит SUID для каталогов не имеет смысла. Однако в последних версиях UNIX биту SGID pios) и sticky-биту (22М) придано новое значение. Исключением из всего сказанного являются права доступа к каталогу пользователя root (i.24y. он может делать в них что угодно и когда угодно. Параграф 22.01 содержит дополнительную информацию о доступе к файлам и каталогам. — ML 1.26 Что можно делать в многопользовательской системе Даже если вы — единственный пользователь, в многопользовательской системе вам доступны многие функции, которые не в состоянии предоставить более простая операционная система. Например, вы можете одновременно запустить несколько программ; при этом не придется ждать, когда завершится выполнение одной программы, чтобы запустить другую. Даже если у вас нет терминала с X Window или рабочей станции с красивыми окнами (i..ii), все равно можно запускать фоновые процессы (1.27). Нет необходимости ожидать завершения выполнения программы, т.к. UNIX позволяет немедленно ввести другую команду. Далее обе программы выполняются одновременно. Более того, наряду с ними могут выполняться другие программы. Вы делите ресурсы процессора с самим собой. Возможность выполнения задания в фоновом режиме позволяет сэкономить время и воспользоваться им для решения других задач, пока система выполняет длительное задание. Предположим, вы получили от своего товарища огромную программу на С и хотите скомпилировать и запустить (52.Ш) ее. Но сидеть и ждать, пока компилятор завершит свою работу, не хочется. Лучше за это время написать товарищу письмо. Поэтому можно запустить компиляцию в фоновом режиме и начать сеанс редактирования в интерактивном режиме: % ее -О bigprogram.c & [1] 2236 % vi letter.txt Символ & означает: "Выполнить задание в фоновом режиме". В следующей строке shell выводит информацию, облегчающую работу с фоновыми процессами. [1] — это номер задания, который используется для управления заданиями (i2.oi). Число 2236 является идентификатором процесса (зв.оз). — ML 1.27 Как выполняется фоновый процесс? Интерпретатор shell постоянно следит за тем, что вводит пользователь, и вызывает другие программы для выполнения заданий, для которых нет соответствующих встроенных команд. Обычно при вызове другой программы интерпретатор shell ожидает завершения предыдущей программы. Амперсанд (&) служит для указания интерпретатору, что ждать завершения не нужно. И Bourne shell, и С shell поддерживают фоновые процессы. Но в системах UNIX, допускающих управление заданиями (izoi) (csh, bash, ksh), имеется ряд дополнительных возможностей для управления фоновыми процессами. Ниже перечислены некоторые из них. Ведение 39
1.28 • Если вы забыли указать, что задание должно выполняться в фоновом режиме, его выполнение можно прервать при помощи сигнала приостановки os.ii), нажав клавиши [CTRL-z]. После этого введите команду bg, чтобы продолжить выполнение задания в фоновом режиме: % find /usr -name tin -print > mine |CTRL-z| Stopped % bg [1] find /usr -name tim -print > mine & • Можно перевести текущее фоновое задание (П.т) в интерактивный режим при помощи команды Jg. Это удобно, если UNIX останавливает фоновое задание, когда требуется ввод с клавиатуры (фоновые задания не могут получать данные, вводимые с клавиатуры). • Команда jobs позволяет получить полный перечень заданий, выполняемых в фоновом режиме. Используя номер, можно перевести задание в интерактивный режим. Можно также уничтожать задания по номерам, а не по идентификаторам процессов. - TOR 1.28 Ошибки при выполнении фоновых процессов 1. При использовании Bourne shell нельзя запустить в фоновом режиме несколько команд, которые в командной строке разделены точками с запятой (s.os). Bourne shell переводит в фоновый режим только последнюю команду и ожидает, пока завершится выполнение предыдущих команд. Это легко проверить с помощью следующей командной строки (в этом случае возникает пауза в 15 секунд, после которой выполняется команда Is): $ sleep 15; Is & В Bourne shell вы не получите приглашения, пока команда sleep (т.щ не завершится. Чтобы, работая с Bourne shell, правильно запустить в фоновом режиме несколько команд, необходимо поместить их в скобки: О 13.07 $ (sleep 15; Is) & Это может показаться ошибкой, но на самом деле является проявлением высокой точности синтаксиса Bourne shell, что несколько раздражает при частом использовании, но очень удобно в программировании. 2. Такие интерактивные программы, как редакторы, не имеет смысла запускать в фоновом режиме. Например, если в С shell набрать команду % vi & [1] 3071 будет получено следующее сообщение: [1] + Stopped (tty output) vi Редактор vi может быть активным только в интерактивном режиме. Целесообразно, чтобы редактор vi пребывал в фоновом режиме остановленным (П.щ. При выполнении редактора vi или другой интерактивной. программы можно быстро вернуться в shell, набрав [CTRL-z] для приостановки программы. После этого интерпретатор shell захватит управление терминалом и выведет знак приглашения. Приостановка редактора vi (Вм) — более эффективный метод, чем использование механизма временного выхода в shell 00.26), поскольку он обеспечивает возврат в исходный интерпретатор shell, а не запуск нового. Чтобы вернуться в редактор, введите команду f g. 40
1.29 3. Многие переоценивают возможности фоновых процессов. Поскольку все фоновые процессы претендуют на одни и те же ресурсы, выполнение в фоновом режиме большого количества процессов может "истощить" систему. Это значит, что каждый процесс будет производиться медленнее. Нам попадались пользователи, которые считали, что если они запустят три программы troff (43.13), то отформатируют три файла быстрее, чем при поочередной обработке. Увы, это ошибка.* 4. Если вы используете Bourne shell, то любой выполняющийся фоновый процесс будет завершен при выходе из системы. Чтобы избежать этого, используйте команду nohup (js.is). 5. Не все созданные процессы имеют одинаковый приоритет. В UNIX формируется очередь процессов в соответствии с их приоритетами. Интерактивные процессы, такие как ввод пользователем команды по приглашению системы, часто получают более высокий приоритет, чем фоновые. Однако можно выполнять фоновый процесс с еще более низким приоритетом, воспользовавшись командой nice (J9.09). Это сравнительно безопасный способ "вежливого обращения" с другими пользователями, а также ускорения вашего активного задания, хотя выполнение фоновых задач при этом несколько замедляется. - TOR, DD 1.29 Когда файл не является файлом ОС UNIX отличается, от большинства операционных систем тем, что является файлово-ориен- тированной. Разработчики UNIX решили, что операционная система станет значительно проще, если все её элементы рассматривать как файлы. Диски, дисководы, терминалы, модемы, сетевые соединения и т.п. — все они в UNIX считаются файлами. В некоторых последних версиях UNIX (например, System V Release 4) даже процессы (js.oi) считаются файлами. Подобно волнам и частицам в квантовой физике, граница между файлами и остальным миром может быть чрезвычайно тонкой: рассмотрение диска как устройства или как особого вида файла зависит прежде всего от того, какие операции будут с ним выполняться. Следовательно, чтобы понять UNIX, нужно понять, что такое файлы. Файл — это цепочка ■"' 'байтов' произвольной длины. Никаких системных ограничений для структуры файла не существует. В UNIX есть только несколько специальных типов файлов (для слежения за дисками и для ряда других целей). Структура файлов определяется программами, которые их используют, а не операционной системой UNIX.** Заголовки файлов также определяются приложением, в котором ведется работа с ними, а не файловой системой UNIX. Программы для UNIX соответствуют одному соглашению. В текстовых файлах для разделения строк текста применяется символ новой строки (перевода строки), а не комбинация символов возврата каретки и перевода строки, как в DOS и Macintosh. Эта разница может вызвать трудности при переносе в UNIX файлов из других операционных систем. Файлы DOS часто перегружены символами возврата каретки (CTRL-m), которые необходимы в этой операционной системе, но излишни в UNIX. Эти символы выглядят просто ужасно при редактировании или выводе на печать и могут нарушить работу некоторых программ в UNIX. Текстовые файлы Macintosh в UNIX имеют вид одной непрерывной строки. Конечно, вы можете воспользоваться утилитами UNIX, чтобы преобразовать файлы DOS и Macintosh для UNIX (см. параграф 1.05). — ML * Раньше системы UNIX выполняли все процессы при помощи одного процессора. В современных системах UNIX может быть несколько процессоров. В таких системах несколько заданий выполняются почти так же быстро, как одно. ** Многие исполняемые файлы — т.е. программы — начинаются с магического числа. Это специальная двухбайтовая последовательность, сообщающая ядру, каким образом выполнять файл. Введение 41
Переадресация ввода-вывода При выполнении большинства UNIX-программ их вывод направляется на терминал. Если программа запрашивает пользовательский ввод, то она читает его с терминала. Можно указать интерпретатору shell перенаправить ввод и вывод на другие устройства, а не на терминал. Например, можно сохранить вывод программы в файле или сообщить программе, что нужно читать ввод из файла, а не с клавиатуры. Shell предоставляет возможность переадресации ввода-вывода. В параграфе 13.01 рассмотрен синтаксис, при помощи которого осуществляется переадресация. Кроме того, программа может брать входные данные из выходного потока другой программы. Такой вид переадресации называется каналом (шу Большинство утилит UNIX предназначены для работы с данными, поступающими по каналу. Программы, каким-либо способом преобразующие такие данные, называются фильтрами. Чтобы понять тонкости переадресации ввода-вывода, необходимо разобраться в понятиях открытого файла и дескриптора файла, (см. параграф 45.20). - JP, TOR Система X Window В 1988 году при Массачусетском технологическом институте (Massachusetts Institute of Technology — MIT), МТИ, была создана организация "МГТ X Consortium" с целью разработки и поддержки независимой от поставщика многооконной системы X Window. (Система была названа X потому, что появилась после системы W, которая была разработана в Стэндфорд- ском университете.) Организация MIT X Consortium со временем отделилась от МТИ и сейчас носит название X Consortium. Многооконная система позволяет разделять большой экран рабочей станции на несколько виртуальных терминалов, или окон. В каждом окне может выполняться отдельное приложение. Хотя интерфейс X Window управляется с помощью мыши, наиболее популярным приложением в оконной системе является простой эмулятор терминала (xterm). Таким образом, X Window позволяет рабочей станции одновременно отображать несколько терминальных сеансов. Это уменьшает значимость многих возможностей UNIX (таких как управление заданиями), поскольку все программы могут выполняться в интерактивном режиме в разных окнах. [Благодаря X Window появился также так называемый X-терминал. Этот терминал позволяет работать с X Window без полноценной рабочей станции. — JF\ Однако не у всех есть система X Window, поэтому данная книга не содержит описания ни этой, ни какой-то другой многооконной системы. Если вам понравится наша книга, рекомендуем обратиться к еще одному источнику — к книге X User Tools издательства O'Reilly & Associates. — TOR Белые пятна В этой книге есть еще одно белое пятно — мы не описываем никаких коммуникационных и сетевых утилит UNIX, в частности электронную почту, World Wide Web, конференции, пакет UUCP, утилиты rlogin, rep, rsh, telnet, ftp, archie, WAIS... и все удивительные ресурсы Internet, которые являются настоящими "приложениями-убийцами" для UNIX. Почему мы опускаем рассмотрение вопросов, связанных с такими важными для UNIX технологиями? Есть только одна причина — объем. При подготовке книги он приближался к 1500 страницам. Мы все время искали темы, которые можно было бы исключить. Многие публикации издательства O'Reilly и других издательств посвящены вопросам работы в сети, поэтому мы оставили эту тему для других книг. В книге можно заметить и другие белые пятна — X Window (iji) и утилиты для -, программирования ядра. Но я думаю, вы согласитесь, что лучше подробнее рассмотреть.
из некоторые вопросы, чем поверхностно коснуться всех возможных тем лишь для того, чтобы можно было заявить, что мы рассказали обо всем. В параграфе 1.33 содержится обзор сетевых средств UNIX. И поскольку мы не хотели изымать некоторые хорошие материалы, которые вполне согласуются с другими параграфами, то в книге осталось несколько примеров работы в сети. Просто мы не даем подробных объяснений и перекрестных ссылок на все команды и понятия. - TOR 1.33 Сети и UNIX Сеть позволяет нескольким компьютерам обмениваться данными и работать сообща. Отчасти благодаря своей открытости UNIX стала одной из операционных систем, в которой сделано много сетевых разработок. Существуют различные методы и программы для работы с сетями в среде UNIX. Мы отмечали, что в книге не освещены вопросы работы в сети (U2). В данном параграфе приведен перечень некоторых популярных сетевых и коммуникационных утилит UNIX. Internet Всемирная компьютерная сеть. Пользователи Internet могут обмениваться файлами, подключаться к другим компьютерам и использовать широкий набор программ и услуг. WWW World Wide Web является быстрорастущей системой информационных служб в Internet. Множество серверов объединяют в гипертекстовую "паутину" документы, графику, звук и пр. Программы просмотра, или броузеры, предоставляют удобный гипертекстовый интерфейс для работы в Internet. (Для многих людей WWW и есть Internet Но UNIX предоставляет и другие способы доступа в Internet.) mail Программа, появившаяся в UNIX задолго до того, как сети стали обычным явлением. Она обеспечивает обмен сообщениями электронной почты между несколькими пользователями. Отправленное сообщение ждет, пока другой пользователь запустит свою почтовую программу. Адресат, получивший сообщение, может сохранить его в файле, напечатать, переслать по другим адресам и тд. Системные программы могут прислать вам сообщение о наличии проблем, а также предоставить необходимую информацию. В свою очередь, вы можете направить программе запрос на получение некоторой информации. Всемирные списки рассылки объединяют пользователей в дискуссионные группы. И это, конечно, не все. Существует множество почтовых программ для UNIX — стандартные, коммерческие и бесплатные. Наибольшую популярность завоевали почтовые программы mailx, Pine, mush, elm и МН (пакет, состоящий из многих утилит, в том числе сотр, inc, show и т.д.). ftp Одно из средств пересылки файлов с одного компьютера на другой при помощи протоколов TCP/IP (чаще всего — по сети Internet). Для запуска программы ftp нужно иметь зарегистрированное имя пользователя и пароль на удаленном компьютере. В режиме анонимного Др-сеанса (52.07) применяется специальная учетная запись с именем anonymous. Этот режим обычно используется для передачи бесплатно распространяемых файлов и программ с некоторого центрального узла. UUCP Аббревиатура от UNIX-to-UNIX Copy (копирование UNIX-B-UNIX). Это семейство программ (ииср (52.07), иих, uulog и др.), предназначенных для передачи файлов и сообщений электронной почты с одного компьютера на другой. UUCP обычно используется при работе с модемами по телефонным линиям. Usenet Usenet не является сетью в полном смысле этого слова. Это набор компьютеров со всего мира, которые обмениваются файлами, называемыми новостями. Система "сетевых новостей" включает сотни интерактивных дискуссионных групп, электронных досок объявлений (BBS), в которых обсуждается все — от технических вопросов до эротики. 43
telnet Данная утилита служит для подключения к удаленному компьютеру через сеть (HanpHMepj Internet) с использованием протоколов TCP/IP. Благодаря ей вы сможете работать на удаленном компьютере так, как будто он стоит у вас на столе. Программа telnet есть во многих операционных системах; она позволит вам подключиться из UNIX к другой операционной системе и наоборот. Специальная версия telnet, tn3270, обеспечивает подключение к мэйнфреймам IBM. rlogin Похожа на утилиту telnet, но используется главным образом в системах UNIX. Специальный файл конфигурации .rhosts, находящийся в вашем начальном каталоге, позволяет подключаться к удаленному компьютеру без ввода пароля. гср Предназначена для копирования файлов с одного компьютера на другой. Она имеет такой же синтаксис командной строки, как и команда ср, за исключением того, что к путевому имени добавляется имя сервера. rsh Запускает удаленный shell для выполнения команды на удаленной системе без входа в нее в интерактивном режиме. NFS Сокращение от Network FileSystem (сетевая файловая система). В действительности это не утилита. NFS и связанные с нею пакеты, подобные NIS (Network Information Service — сетевой информационный сервис), позволяют системному администратору монтировать на вашем локальном компьютере файловую систему удаленного компьютера. Удаленную файловую систему так же просто использовать, как и локальную. write Посылает сообщение на экран другого пользователя. При помощи программы write два пользователя могут вести беседу. talk Более сложная программа, чем write. Она делит экран на две части и позволяет двум пользователям набирать текст одновременно. Программу talk можно использовать в сети, хотя не все ее версии понимают друг друга. - JP Недостатки UNIX К сожалению, те же факторы, из которых проистекают преимущества UNIX, являются причиной недостатков этой системы. Открытая операционная система, обремененная полезными надстроечными утилитами 25-летней давности (l.oi), обречена иметь невероятное количество несогласованных и повторяющихся функций. Это может сбить с толку даже опытного пользователя. Достаточно понаблюдать за "войной наездов" в Usenet (изз), чтобы представить, насколько невелико взаимопонимание даже среди специалистов. Особо остро стоит проблема дубликатов среди функций. Можно ли, например, доказать целесообразность существования утилит (set (s.ii) и stQ (4i.m), каждая из которых предназначена для задания параметров последовательного порта? Конечно, нет. Эти программы — результат независимых усилий для решения взаимосвязанных задач. Дублирование функций этих утилит является непреднамеренным, и если бы процесс их разработки был управляемым, то он завершился бы появлением одной утилиты с унифицированным интерфейсом. - TOR
Часть первая Работаем дома Сделайте одолжение. Не читайте все подряд. Листайте книгу, останавливаясь на тех местах, которые представляют для вас интерес. Читайте параграф до тех пор, пока интересная гиперссылка не заставит вас перейти к другому блоку информации. Постарайтесь каждый день открывать для себя что-нибудь новое. Найдите в книге нечто, что заставит вас улыбнуться. Потом отложите книгу до следующего раза, когда снова захотите отдохнуть. Если вы все же решите читать подряд, то в следующих четырех главах вы найдете материал о входе и выходе из системы, об организации своего начального каталога, о настройке терминала, а также о переменных shell и среды. - TOR
2 Вход в систему 2.01 Настройка shell Вероятно, вам известно, что интерпретатор shell можно настроить на желательный режим работы при помощи переменных shell и среды (6.08,6.01), псевдонимов команд (шу, функций shell (laoi) и т.д. В любой момент можно вручную задать значения переменных и создать псевдонимы команд (пользовательские команды), однако shell "забудет" выполненные вами установки после выхода из системы. Чтобы при каждом входе в систему использовать одни и те же установки, сохраните соответствующие команды в специальном файле конфигурации shell (2.02), находящемся в начальном каталоге. Такой файл служит не только для установки значений переменных shell. Он позволяет выполнить любую команду UNIX при входе и выходе из системы. Все это поможет сэкономить время и более эффективно провести сеанс работы в системе. - JP 2.02 Файлы конфигурации интерпретатора shell: что, гце и почему Чтобы понять назначение файлов конфигурации, нужно уяснить, что интерпретатор shell может работать в двух режимах: либо с использованием всех установок, заданных в пользовательском файле инициализации (login shell — регистрационный интерпретатор), либо без применения этих установок (non-login shell — нерегистрационный интерпретатор). При входе в систему программа login обычно запускает интерпретатор shell. Эта же программа устанавливает специальный флаг (5Ш), который сообщает интерпретатору shell, что он является регистрационным. Если этот флаг не установлен, интерпретатор shell не будет работать как регистрационный. При открытии нового окна в многооконной системе интерпретатор shell может быть запущен как регистрационный, но не во всех случаях, что зависит от конфигурации. (Например, команда xterm -Is запускает регистрационный интерпретатор shell в окне xterm (ui), а команда xterm +ls — нет.) При подключении к системе при помощи таких программ, как ftp или uucp, регистрационный интерпретатор shell обычно не запускается. Порожденный интерпретатор shell не может быть регистрационным. Как определить, работает ли shell как регистрационный интерпретатор? К сожалению начинающих, ответ выражается в виде фраз типа "это зависит от...". Ответ имеет смысл только в том случае, если вы более или менее знакомы с системой. При первом входе в систему необходимо, чтобы интерпретатор shell активизировался в регистрационном режиме с установкой таких параметров, как тип терминала (5.02, 5.0З) и т.п. Другие интерпретаторы, запущенные на том же терминале, не должны быть регистрационными, что позволяет избежать повторного выполнения команд инициализации, которые должны выполняться только один раз. Для различных интерпретаторов shell характерны разные методы вызова программ инициализации в первый и последующие разы. Этому вопросу и посвящена остальная часть данной главы. *«>Д в систему 47
гм Забегая вперед, замечу, что во всех известных мне интерпретаторах shell операторы-скобки (13.07) предотвращают чтение любых файлов инициализации. При использовании этих операторов происходит запуск еще одного экземпляра текущего интерпретатора. Скобки часто называют операторами запуска порожденного интерпретатора shell (subshell). Такие интерпретаторы не выводят приглашения и завершаются после выполнения задачи. Я рекомендую вам ознакомиться с информацией обо всех интерпретаторах shell. Затем поэкспериментируйте с файлами конфигурации, пока интерпретаторы не начнут функционировать должным образом. Bourne shell Самый старый из интерпретаторов, Bourne shell, имеет один файл, который читается при входе в систему, — .profile. Этот файл находится в вашем начальном каталоге. Записывайте туда также все свои команды. Bourne shell не читает файл .profile при запуске порожденного интерпретатора shell. Информация для порожденного интерпретатора находится в переменных среды (6.oi), значения которых устанавливаются при первом входе в систему или посредством команд, введенных после входа. С shell В распоряжении пользователя С shell есть три файла конфигурации: • .cshrc — читается при каждом запуске С shell, в том числе при временном выходе в shell и выполнении сценария.* В этот файл нужно включать команды, которые должны выполняться при каждом запуске С shell. Например, такие переменные С shell, как cdpath (H.05) и prompt (7.01), должны находиться как раз в этом файле. Здесь также задаются псевдонимы команд оо.о2). Значения этих переменных не передаются порожденному интерпретатору shell, поэтому должны определяться в файле .cshrc. • .login — читается при запуске интерпретатора shell в режиме регистрации. В нем нужно задать: — переменные среды (t.oi) (которые UNIX автоматически передает порожденным интерпретаторам shell); — такие команды, как tset (5.оз> и stQ (s.w, 4Ш)\ — команды, которые должны выполняться при каждом входе в систему (проверка почты и новостей озз), запуск программы fortune (з.оз), просмотр календаря и т.п.). Обратите внимание, что файл .cshrc читается раньше, чем файл .login. • .logout — читается при завершении работы регистрационного интерпретатора shell. В параграфе 2.07 даны рекомендации, как читать файл .logout из нерегистрационного интерпретатора shell. Кот shell Korn shell во многом похож на Bourne shell. При запуске Кот shell в регистрационном режиме (2.08) сначала читается файл .profile. В этом файле значение переменной среды ENV (б.оз) может устанавливаться равным путевому имени определенного файла (обычно $HOME/.fohrc). Затем любой экземпляр shell (в том числе порожденный) при инициализации будет читать файл $ENV, прежде чем выполнять другие команды. bash В bash сочетаются свойства Bourne shell и С shell. При регистрации в системе интерпретатор bash читает файл .bashjrofile, .bashjogin или .profile. Порожденный — но не регистрационный — интерпретатор shell читает файл .bashrc в вашем начальном каталоге. При выходе из системы bash читает файл ,bash_logout. Можно задать в файле конфигурации команду trap (3.02) для управления нерегистрационным интерпретатором shell. * При написании сценария для csh лучше использовать опцию -/ чтобы предотвратить чтение сценарием файла ; .csJirc. Следует воздерживаться от использования сценариев в csh (47.02). I 48 Часть первая. Работаем дош{
гм . . tcsh Интерпретатор tcsh подобен С shell за одним исключением: если • сохранить в начальном каталоге файл .tcshrc, интерпретатор shell данного вида будет читать его вместо файла .cshrc. - JP 2.03 Что происходит при выполнении файлов конфигурации shell? При выполнении таких файлов конфигурации интерпретатора shell, как..login и .profile, обычно происходит следующее: • Задается последовательность поиска (а.от). • Задается тип терминала (5.оз) и его параметры (5.09, 41 .оз). • Устанавливаются значения переменных среды (б.оу, которые могут понадобиться часто выполняемым программам или сценариям. • Активизируется одна или несколько команд, выполнение которых необходимо при входе в систему. Например, если в вашей системе программа login не выводит каждодневного сообщения, это можно задать в файле конфигурации. Многие пользователи любят выводить забавные пожелания (з.оз). Можно задать выполнение программы who (si.m) или uptime (39.07) для получения информации о системе. В С shell файл .cshrc предназначен для выполнения установок, которые будут применяться во всех экземплярах shell, а не только при регистрации в системе (51.Щ. Например, в этом файле можно задать псевдонимы команд (ю.ю), которые будут доступны в каждом экземпляре интерпретатора shell. Даже новички смогут написать простые файлы .profile, .login или .cshrc. Сложность заключается в обеспечении правильной работы этих сценариев инициализации. Вот некоторые задачи, решаемые в таких файлах: • создание пользовательского символа приглашения (параграф 7.01); • настройка пользовательских файлов конфигурации для разных машин (параграф 2.13); • выполнение различных установок с учетом типа используемого терминала (параграф 2.12); • -задание вывода только измененного сообщения дня (параграф 2.15); • выполнение всех перечисленных операций без замедления процесса входа в систему (параграф 2.05). - TOR 2.04 Изменение конфигурации пользовательской учетной записи Shell является средством взаимодействия пользователя с системой UNIX. Если вы совершили грубую ошибку при изменении файла .cshrc, .login или .profile (2.02) либо пароля, будет трудно войти в систему и исправить такую ошибку. Перед изменением файлов конфигурации следует зарегистрироваться в системе под таким же именем, но с другого терминала. Используйте этот сеанс для проведения изменений. Затем повторно войдите в систему с нового терминала, чтобы проверить корректность изменений. У вас нет многооконной системы или другого терминала? Того же результата можно добиться, если опять войти в систему с того же терминала при помощи утилит rlogin или telnet (Ш). Я имею, в виду следующее: somehost% vi .cshrc ...Редактирование файла .cshrc... Яюд в систему 49
2.05 somehost% rlogin localhost ...Вход в систему под тем же именем... Сообщение об ошибке somehost% logout Connection closed. somehost% vi .cshrc ...Исправление ошибок... При отсутствии утилит rlogln и telnet практически ту же функцию выполняет команда su - имя пользователя (22.22). - JP 2.05 Как ускорить процедуру входа в систему? В начале 80-х, когда я только начинал работать с С shell, я создал потрясающие файлы .cshrc и .login (2.02) со всевозможными удобными настройками. Псевдонимы команд, команды проверки почты, календари, фоновые сценарии shell для отслеживания самых разнообразных вещей... Это, скажу вам, было здорово! Правда, не обошлось без исключения, каковым являлась процедура входа в систему; Я работал на перегруженной системе VAX 11/750. Вход в систему мог занять несколько минут, с момента получения приглашения login: до появления приглашения командной строки %. (... Вообще-то было намного более веселое приглашение, но это уже другая история. :-)) С shell, с моей точки зрения, довольно медленно читает длинные файлы .cshrc и .login, особенно при установке псевдонимов команд. Поэтому я изучил некоторые способы более быстрого входа в систему, которые оказались особенно удобными, когда я работал на чужом терминале и нужно было быстро регистрироваться. Вы можете не использовать эти приемы, однако я надеюсь, что они подскажут вам новые идеи относительно ускорения входа в систему. К подобным приемам вы сможете обратиться и при использовании других интерпретаторов shell, конечно, применяя характерные для них команды и синтаксис. Быстрый вход в систему Добавьте в начало файла .cshrc процедуру "быстрого входа в систему". Как только С shell начнет работать и установит некоторые важные параметры, эта процедура спросит у вас, хотите ли вы немедленно получить символ приглашения. Если ответить утвердительно, она запустит другой интерпретатор С shell с опцией -f. В результате порожденный интерпретатор shell (за.оу будет вынужден пропустить файл .cshrc, вследствие чего цикл не образуется: login: jerry Password: Last login: Tue Jan 19 12:34:56 PST 1999 Answer у for quick login or RETURN for standard: у For standard login, type 'exit 77'. % mail bigboss Subject: I'm on my way Carol, I'm leaving for the meeting now. Sea you by 10:00. % ICTRL-dl login: После этого я могу выполнить несколько команд. При вводе команды [CTRL-d] или exit происходит выход из "быстрого" интерпретатора shell, а также уничтожение регистрационного интерпретатора shell. Чтобы продолжить работу на том же терминале, я набираю exit 77. Это заставляет "быстрый" порожденный интерпретатор shell возвратить код завершения (Ш7), равный 77. Если такое значение кода завершения обнаружено при проверке в файле .cshrc, то процесс моего входа в систему продолжится, и будет прочитана остальная часть файлов .cshrc и .login. Ниже приведена начальная часть файла .cshrc, посредством которой реализуются описанные действия. 50 Часть первая. Работаем дом)
2.05 # эти команды выполняются только в интерактивном режиме if (! $?prompt) goto cshrc_end # БЫСТРЫЙ ВХОД: if (! $?LOGGEDIN) then set path = (/bin/ usr/ucb/ usr/local/{bin,mh) {/usr,~}/bin .) echo -n " Answer у for quick login or RETURN for standard: " if ("$<" =~ y*) then echo "For standard login, type "exit 11'." csh -f #ВЫХОД из .cshrc... ЭТО ГРУБЫЙ ПРИЕМ, НО ОН РАБОТАЕТ: if($status != 77) kill -9 $$ endif endif setenv LOGGEDIN yes ...Остальная часть файла .cshrc. cshrc end: He забудьте, что в начале необходимо использовать условную конструкцию i f ($?prompt) (2.09), чтобы предотвратить чтение этой команды в неинтерактивных интерпретаторах shell. Если этого не сделать, то неинтерактивные интерпретаторы shell при выполнении таких заданий, как at, могут зависнуть, ожидая ответа на вопрос о том, какую процедуру входа в систему выполнять, или вообще не смогут работать. Пользовательские команды и командный файл Вероятно, у вас есть набор пользовательских команд (псевдонимов команд) или команд инициализации, которые используются только при работе над определенными проектами. Если такие процедуры инициализации не должны выполняться при каждом входе в систему, соответствующие команды можно сосредоточить в отдельном.файле. Создайте для команды псевдоним, например, с именем setup, с помощью которого этот файл читается в нужный момент. Вводите команду setup только тогда, когда необходимо выполнить дополнительную инициализацию. Строка объявления псевдонима имеет следующий вид: ~ 14.11 alias setup ' if (! $?setup) source ~/lib/cshrc2' source 44.23 Начало файла ~/lib/cskrc2 должно выглядеть так: set setup # переменная для предотвращения повторного чтения файла cshrc2 alias foo bar Первая строка файла ~/lib/cshrc2 устанавливает значение переменной среды, которая предотвращает чтение файла пользовательской командой setup в данном экземпляре интерпретатора shell. Это позволяет сэкономить время, если вы забудете, что уже выполняли команду setup. Ежедневная инициализация Существуют команды, выполняемые только один раз в день — при первом входе в систему. Например, у меня есть программа, которая выводит план на текущий день, напоминает о днях рождения и т.п. Эти операции выполняет условная конструкция в файле .login, приведенная ниже: в»ВД в систему 51 if47.03 ! $? 47.04 О 9.05 $<Г =~ 47.04 kill 3S.10 SSS.19 setenv 6.01
2.06 $date(n] 47.0S # Запомнить название дня в $date[l], месяца — в $date[2], и т.д.; .set 6.08 sefc date- (' date ') ...' 9.1Л # если сегодняшний файл инициализации не существует, то он создается и .# выполняется: -е 47.04 if (! -e -/tmp/, setup. $date [3] ) then touch 21.07 touch -/tmp/, setup. $date[3] dq_calendar .. .Другие ежедневные команды инициализации... endif unset M unset date В этой условной конструкции для хранения текущей даты и создания в каталоге tmp пустого файла с именем типа ,setup.23 используется массив интерпретатора csh (47.0s). Если такой файл уже создан (допустим, 23 июня), то последующие команды инициализации в этот день выполняться не будут. У меня есть также программа, которая периодически удаляет файлы с запятой в имени (23.20,23.22). Таким образом, файл ,setup.23 будет удален к 23 числу следующего месяца. То же можно сделать с помощью файла .logout (3.01,3.02). - JP 2.06 Использование полного путевого имени в файлах конфигурации Часто в файлах конфигурации интерпретатора shell (2.02) используются следующие ошибочные строки: source .aliases '—'9.16 echo "Logged in at %date~" » login.log Что здесь неверно? В обеих строках употребляются относительные путевые имена ол) файлов (.aliases, .login.bg). Так как эти файлы находятся в начальном каталоге, то команды работать не будут. Дело в том, что при запуске порожденного интерпретатора shell (М04) не из начального каталога читаются файл .cshrc или .kshrc и переменная ENV, находящиеся в том каталоге, откуда запускался интерпретатор. Такие же проблемы возникают при использовании команд source и . (44.23) для чтения файлов .profile и .login не из начального каталога. Используйте полные путевые имена. Как отмечалось в параграфе 14.11, для указания путевого имени вашего начального каталога можно применять тильду (~) или переменные среды НОМЕ и LOGDIR: source -/.aliases echo " "Logged in at %date4" » -/login.log - JP 2.07 Выборочное чтение файлов конфигурации С shell читает свои файлы .cshrc, .login и .logout в определенных случаях (2.02). Только регистрационные интерпретаторы shell (2.0s) читают файлы .login и .logout. Прежде, когда csh только появился, это ограничение срабатывало прекрасно. Для интерпретатора shell, который запускался при входе в систему, устанавливался соответствующий флаг, и этот интерпретатор читал все три файла. Если из регистрационного интерпретатора запускались другие экземпляры (временные, для сценариев и т.п.), они читали только файл .cshrc. В настоящее время в UNIX применяются интерактивные интерпретаторы shell, запущенные щзи помощи многооконной системы (например, xterm рл)), удаленные интерпретаторы shell (например, /тй (из)), а также другие интерпретаторы, для которых могут понадобиться некоторые установки из файлов .login и .logout. Если эти интерпретаторы не являются регистрационными, они будут читать только файл .cshrc. Как быть в такой ситуации? Включение всех команд инициализации в файл .cshrc не является правильным решением проблемы, поскольку все порожденные интерпретаторы shell (38.04) также читают этот файл, S2 Часть первая. Работаем дош
2.08 а нам совсем не нужно, чтобы такие команды инициализации терминала, как. tset, выполнялись при временном выходе в shell. Чтобы решить проблемы, возникающие при входе в систему, поместите большинство команд инициализации в файл .cshrc, а не в файл .login. После того, как все команды, относящиеся только к процедуре инициализации интерактивного интерпретатора shell, будут прочитаны из файла .cshrc, установите переменную среды (6.oi) ENVJSET в качестве флага. (В данном случае имя не имеет никакого значения — вы даже можете выбрать любое другое имя.) Shell передаст переменную-флаг порожденным интерпретаторам. Команды из файла .cshrc могут проверять переменную: если она существует, то команды, предназначенные для выполнения регистрационным интерпретатором shell, пропускаются. Таким образом, можно предотвратить выполнение этих команд при временном выходе в shell. Ниже приведены фрагменты файла .cshrc, иллюстрирующие изложенный выше теоретический материал: ...Обычное содержимое файла .cshrc if ($?prompt && ! $?ENV_SET) then # Выполнение команд, которые используются в файле .login:, setenv EDITOR /usr/ucb/vi tset setenv ENV_SET done endif В файл .login нужно поместить комментарий о том, что вы сделали. ■'■ Файл .logout; Очевидно, должен читаться только в одном случае — при выходе из последнего интерпретатора shell (самого высокого уровня). Даже если интерпретатор shell самого высокого ■- ■ уровня не является регистрационным,.его все равно.можно заставить читать файл .logout. Это ■,■ ■ _■■ делается так. Вместе с предыдущими исправлениями файла .cshrc добавьте пользовательскую команду, которая читает файл .logout после ввода команды exit Установите также переменную " ignoreeof (3.05), чтобы выход из системы осуществлялся только по команде exit. В результате .... соответствующий фрагмент файла .cshrc будет иметь следующий вид: ...-.'■'■■. if ($?prompt && ! $?ENV_SET) then # Заставить все интерактивные интерпретаторы shell высшего уровня # читать файл .logout , set ignoreof "'edt7fl.ee alias exit 'source ~/. logout; ""exit1 endif - JP 2.08 Распознавание регистрационного интерпретатора shell При первом входе в систему с терминала обычно запускается регистрационный интерпретатор shell. Именно в нем выполняются общие установки — конфигурирование терминала, установка системных переменных и т.д. В процессе инициализации С shell Читает файл .login, a Bourne shell — файл .profile (bash может читать другие файлы). Другие интерпретаторы shell могут быть порожденными (38.04) (запущенными из регистрационного интерпретатора shell), а могут быть самостоятельными, запущенными при помощи .-..-.■. команд at (40.03), rsli (1.зз) и т.п. Такие интерпретаторы не читают файлов .login или .profile. Чтобы распознать режим работы интерпретатора shell, добавьте одну строку в начало файла .login или .profile, инициирующую переменную shell (вщ loginshell: set loginshell=yes ...для csh loginshell-yes ...для sh if 47.03 $? 47.04 ВЩ в штему S3
2.09 Теперь для определения вида интерпретатора shell можно использовать следующие условные конструкции: it47.m if ($?loginshell) ...для csh %%'м if [ -n "$loginshell" ] ...для sh [...] 47.04 Это срабатывает, потому что только регистрационный интерпретатор shell читает файлы .login и .profile. Еще одно решение вы найдете в параграфе 7.09. - JP 2.09 Ускорение запуска С shell С shell может быть запущен несколькими способами: при временном выходе в shell (зогл), а также в результате выполнения команды su (22.22), сценария или запускаемого командой at задания (4o.oi) и т.п. В каждом случае csh читает файл .cshrc в вашем начальном каталоге. Некоторые интерпретаторы shell могут быть неинтерактивными. Это значит, что никаких команд вам вводить не придется — интерпретатор выполняет единственную команду или читает команды из файла сценария (i.os). Обычно файл .cshrc содержит такие команды, как alias (Ю.02), set cdpath (I4.0S) и т.д., которые используются только в интерактивном режиме. Чтение этих команд неинтерактивными интерпретаторами shell окажется пустой тратой времени. Вы можете указать интерпретатору shell пропустить эти команды. Сформируйте свой файл .cshrc следующим образом: # КОМАНДЫ ДЛЯ ВСЕХ ИНТЕРПРЕТАТОРОВ С SHELL: set path = (...последовательность поиска...) \M%7nj ^f '' $?prompt) goto cshrc_end # КОМАНДЫ ТОЛЬКО ДЛЯ ИНТЕРАКТИВНЫХ ИНТЕРПРЕТАТОРОВ С SHELL: alias foo bar set cdpath = (~ -joe/project) cshrc_end: ^^.1/, Выражение ! $?prompt истинно только в неинтерактивных интерпретаторах shell, в которых jkj/^4 переменная prompt не установлена. В таких случаях команда goto cshrc_end заставляет Щг интерпретатор shell перейти в конец файла к строке с меткой cshrc_end:. [2.10] Если вы хотите установить собственную строку приглашения (7.oi), не забудьте сделать это после проверки значения переменной prompt, иначе такая проверка не будет успешной. Примечание: В некоторых книгах дается совет использовать иную условную конструкцию: if (! $?prompt) exit # команды только для интерактивных интерпретаторов shell: В этом случае С shell некоторых версий, встретив команду exit в файле .cshrc, завершает работу. Использование команды goto cshrc_end обеспечивает лучшую переносимость. В параграфе 7.03 рассказывается еще об одной проблеме, которая решается при помощи условной конструкции с выражением $?prompt. - JP 54 Часть первая. Работаем дом
2.10 2.10 Ошибки при проверке значения переменной prompt Многие пользователи вставляют в файлы .cshrc условные конструкции If (! $?prompt) о-щ. Однако некоторые поставщики систем стали добавлять средства для обхода этой конструкции. Например, некоторые версии команды which (so.os) так инициализируют переменную prompt, что определения псевдонимов команд, "спрятанные" внутри условной конструкции, становятся видимыми. Мне встречалась также версия команды at, которая запускает интерактивный shell для выполнения заданий. Если команды, которые должны выполняться только в интерактивных интерпретаторах shell или при входе в систему, поместить после строки if (! $?prompt), могут возникнуть проблемы. Существует несколько способов их решения. Выбор способа зависит от характера проблемы. • Версия команды which, находящаяся на компакт-диске, работает без чтения файла .cshrc, поэтому с ней не должно возникать каких-либо трудностей. • Вот как можно предупредить чтение определенной части файла .cshrc стандартной командой which. При первом входе в систему устанавливается переменная среды (s.oi) CSHRC_READ. Эта переменная будет передаваться во все порожденные интерпретаторы shell (38.04) (как и в процесс, созданный командой which). В порожденном интерпретаторе shell условная конструкция if ($?CSHRC_READ) вызывает переход к концу файла .cshrc: if (! $?prompt) goto cshrc_end # ЭТИ КОМАНДЫ ЧИТАЮТСЯ ТОЛЬКО В ИНТЕРАКТИВНЫХ ИНТЕРПРЕТАТОРАХ SHELL: alias foo bar if ($?CSHRC_READ) goto cshrc_end # ЭТИ КОМАНДЫ ЧИТАЮТСЯ ТОЛЬКО ПРИ ВХОДЕ В СИСТЕМУ: setenv CSHRC_READ yes cshrc_end: • Если ваша версия команды at (*о.оз) содержит ошибки, из-за которых задания выполняются в интерактивных интерпретаторах shell, создайте собственную команду at (io.oi), которая временно устанавливает переменную среды AT перед вызовом настоящей команды at. Добавьте в файл .cshrc условную конструкцию, обеспечивающую выход, если переменная А Т установлена: # В МОЕЙ ВЕРСИИ UNIX КОМАНДА at ЗАПУСКАЕТ ЗАДАНИЯ В ИНТЕРАКТИВНЫХ # ИНТЕРПРЕТАТОРАХ SHELL. # РЕШЕНИЕ ЗДЕСЬ И В ПСЕВДОНИМЕ КОМАНДЫ at НИЖЕ: if ($?AT) goto cshrc_end 01397 alias at '(setenv AT yes; Nat \!*)' \at 10.06 cshrc_end: Современные версии команды at сохраняют копию вашей среды и восстанавливают ее после запуска задания. К тому моменту переменная среды AT будет установлена. С shell пропустит фрагменты файла .cshrc, которые не должны выполняться. Это грубый прием, но он действенный. Возможно, описанные способы не позволят решить все проблемы, возникающие в вашей версии UNIX, но я надеюсь, что они послужат хорошей основой при выработке новых способов. - JP *Щ в систему 55
2.11 2.11 Ускорение запуска ksh и bash Используете ли вы Кот shell и содержится ли в файле .profile строка "Е1ЯУ=файл " (2.02) (файл инициализации, заданный с помощью переменной среды ENV)? Если вы пользуетесь bash, то у вас, наверное, есть файл конфигурации .bashrc или .bashjlogin. Вы можете столкнуться с той же проблемой ащ, что и пользователи С shell, работающие с файлом .cshrc: неинтерактивные интерпретаторы. shell читают определения псевдонимов команд и другие строки сценария, которые должны читаться только интерактивными интерпретаторами shell. Ускорьте работу файла инициализации, добавив следующую условную конструкцию: case 44.06 case $- in *i*);; *) return 0;; esac # ЭТИ КОМАНДЫ ЧИТАЮТСЯ ТОЛЬКО В ИНТЕРАКТИВНЫХ ИНТЕРПРЕТАТОРАХ SHELL: Условная конструкция проверяет, установлена ли опция shell -i. Если не установлена, возвращается значение 0 и происходит выход из файла. -JP -: 2.12 Автоматическая инициализация различных терминалов При использовании терминалов некоторых типов их инициализация может оказаться довольно сложной. Например, мой X-терминал при нажатии клавиши в правом верхнем углу клавиатуры посылает символ стирания (backspace), а другой терминал при нажатии той же клавиши генерирует символ удаления (delete). В результате мне приходится применять команду stty erase (s.pgj для автоматической установки символа, удаления. У вас может также возникнуть желание иметь в своем распоряжении набор программ-календарей, которые запускаются при входе в систему с терминала, стоящего на вашем столе, "но не выполняются при быстром входе в систему (2,os) с других терминалов. Предлагаем несколько идей относительно автоматического изменения процедуры входа в систему. Одни примеры рассчитаны на С shell, в них рассматривается использование операторов switch (47.00 и if (47.03). Другие ориентированы на- Bourne shell (рассматривается применение операторов case (44.0S) и if (44.os)). Даже если вы работаете с интерпретатором shell другого вида, наши советы окажутся полезными для вас; нужно всего лишь учесть специфику синтаксиса. • Для инициализации терминала (установка переменной среды TERM (5.щ, задание символа \ удаления и т.п.) достаточно команды tsel (s.oj). • Если для каждого терминала переменная TERM устанавливается особым образом, можно ■ добавить в файл .login условную конструкцию, подобную приведенной ниже: ] switch ($TERM) I case VT100:• | ...выполнить команды для VT100 % breaksw "1 case xxx: Я ...выполнить команды для терминала xxx щ breaksw l default: | ...выполнить команды для других терминалов endsw И т.д. • Если вы входите в систему с удаленного терминала о.зз) или из окна системы X Window о.Щ команда who am i может вывести имя сервера и (или) информацию в скобках, относящую^ к данному окну: bash$ who am i jpeek pts/6 Jul 17:30 (www.jpeek.com:0.0) 56 Часть первая. Работаем №
щ (Длинные имена серверов иногда усекаются. Проверьте имя, прежде чем воспользоваться данной рекомендацией.) Если информация в скобках окажется полезной, добавьте подобные команды в свой, файл .profiler. cax44.es case ""who am i | sed -n ' s/. * (\ (. *\) )/\l/p' ~ " in. . \(Л)\1-'*"? . *0.0) ...выполнить команды для окна ОХ Window)-} mac2*) ...выполнить команды для сервера mac2.foo.com ;; "-"■) ...нет информации (возможно, это не удаленный вход в систему) ;; *) ... выполнить команды для других ситуаций ;; • esac В данном случае редактор sed (34.24) используется для передачи оператору case заключенного в скобки вывода команды who am i. Если вывод содержит символы *0.0, обрабатываются строки, заканчивающиеся символами 0.0, а если — тас2*, обрабатываются строки, которые начинаются символами тас2, Пустая строка означает, что редактор sed не обнаружил скобок. Вариант * соответствует остальным случаям. • Если вы знаете, что порты с определенными номерами служат для входа в систему по определенному протоколу, можно проверить и номер порта. Например, во многих системах используются порты ttypO, ttyql и т.п. в качестве сетевых портов для утилит rlogpivi telnet (U3). Выбор опций в следующей условной конструкции зависит от имени порта: case ""tty"" in /dey/tty[pqrs]?) # rlogin, telnet: /dev/tty02) # вход с локального терминала: "not a tty") ;; ...это сеанс входа в систему не с- терминала,- ничего ле делать • _•■ esac- . . • - • Некоторые программы, устанавливают определенные системные переменные, Например, система X Window устанавливает системную переменную DISPLAY. (Чтобы узнать, в какой системе вы работаете, выполните команду em или printenv (6.01) для определения значений " Переменных среды.) Можно построить следующую условную конструкцию: 1Г47.Ю -.- if- (5.7DISPLAY) then S'47.04 ' '- ":. # в. системе X Window else if ($?WIN_PARENT) then ; . # в системе. SunView else endif • В вашей системе может присутствовать файл /etc/ttytab или /etc/ttys, втсотором перечислены характеристики всех терминальных портов. Строки в таком файле имеют приблизительно следующий вид: console "/usr/etc/getty std.9600" vtlOO on local ttyOO "/usr/etc/getty std.9600" ■ . dialup .:.off local ttyOl "/usr/etc/getty std.9600." plugboard off local ttypO none network off (Порт ttypO имеет тип network и используется утилитами xterm р.зц, telnet (1.33) и т.п.) Вы можете использовать выходную информацию команды tt% (з.щ для поиска характеристик текущего терминала в этом файле. Выходная строка команды tty имеет вид /dev/имя или /dev/pts/имя. Для поиска типа вашего терминала в файле нужно взять последнюю **жту ■■-•- ---■ -- — " ' ""' 57
2.13 часть строки. Например, в bash и ksh следующие три строки запишут тип терминального порта (vtlOO, plugboard и т.п.) в переменную ttykind: tty= ~ tty' $(..#..> 9.07 ttytail=$(tty#/dev/} awkJJ./7 ttykind=~awk '$1 ~ "'$ttytail'" {print $3}' /etc/ttys' • Вы можете воспользоваться древней и забытой программой (set (S.oj), позволяющей определить тип терминала и правильно его инициализировать. Еще одна программа, определяющая тип терминала, — qterm (s.os). Вы найдете ее на прилагаемом компакт-диске. - JP 2.13 Файл .cshrc.$HOST для инициализации различных серверов Я каждый день работаю на разных машинах. Часто приходится по-разному выполнять те или иные установки в системе Linux и рабочей станции SPARCstation. Кроме того, может возникнуть необходимость выполнить различные установки для разных серверов одного типа. В моем файле xshrc есть следующая условная конструкция: setenv 6.01 setenv HOST " ' uname - n " " №47.03 if (~e ~/lib/cshrc.hosts/cshrc.$HOST) then ~ M,// source ~/lib/cshrc.hosts/cshrc.$HOST endif При входе в систему на машине с именем (So.07) bosco, на которой имеется файл: ~/lib/cshrc.hosts/cshrc.bosco, я могу воспользоваться командой source (44.23) с целью настройки среды для данной машины. Вот несколько примеров установок, которые можно выполнить, в файле .cshrc.$HOST. * • Последовательность поиска (я.07>: на одних машинах в переменной PATH должен быть| каталог /usr/locat/bin, а на других —/opt. Эта же последовательность хранится в переменной' shell cdpath (i4.os). • Установки терминала (5щ: Для удаления символов мне удобно использовать клавишу,! находящуюся в правом верхнем углу клавиатуры. Иногда там находится клавиша]' [BACKSPACE], а иногда — [DELETE]. Я настраиваю терминал так, что, какая бы клавиша: там не находилась, она ведет себя как клавиша удаления символов. i • Другие переменные shell (бщ и переменные среды (6.oi) могут быть самыми разнообраз-f ными. При запуске программы на специфической машине, где используется несколько! особых переменных среды, нет необходимости устанавливать их каждый раз и занимать' память, раз они используются только на этой машине. В общем, изложенный подход позволяет задать специфические установки для разных MaunraJ а не писать в файлах xshrc и .login целый ряд операторов switch (47.06) или if (47.оз). - DS 2.14 Сценарий motd.diff: новые строки в регистрационном сообщении В одной из версий UNIX, с которой я работал, при входе в систему выводилось слишко уж длинное сообщение — на весь экран. Оно содержало подробный список старых програм^ которые я пытался запустить в течение последних трех недель. Вскоре я перестал обра на него внимание. Но я знал, что рано или поздно системный администратор вставит в сообщение предупреждение об останове системы, и я его не замечу... Решить данную проблему позволяет следующий сценарий. Его запуск осуществляется из ф .login. Каждый раз при входе в систему сценарий сравнивает текущий файл /etc/motd с который создан во время предыдущего входа в систему. Если появились новые строки, выводятся на экран, и сценарий делает паузу, предоставляя возможность ознакомиться с login: jpsek Password: 58 Часть первая. Работаем I
us Additions to system message-of-the-day: ===== 9/5/99 ===== The system will be down for maintenance from 9 to 11 tonight. Hit RETURN to continue: Если новых строк нет, процедура входа в систему не прерывается. Этот прием хорошо срабатывает в системах, которые ищут файл .hushlogin в начальном (ф) J каталоге и не выводят сообщение, если файл существует. Инсталлируйте эту программу с компакт-диска, а затем добавьте в свой файл .login или .profile команду motd.diff. mtd.diff Команда motd.diff использует команду diff(2a.oi) для сравнения файла /etc/motd с файлом вашей предыдущей регистрации на данном сервере (файл сохраняется под именем Aast.motd.uMH_cep- вера в вашем начальном каталоге). Сценарий проверяет, появились ли в этом файле новые строки, путем поиска символа > в начале каждой строки на выходе команды diff. diff $lastmotd /etc/motd > $temp if grep "Л>" $temp >/dev/null #команда diff помечает новую строку символом > then ...вывод строк Команда сотт (2S.I2) также выводит строки, которые были добавлены в файл. Однако ее действие распространяется только на файлы, имеющие регулярную структуру; на неупорядоченные файлы эта команда не воздействует. Оператор if (44.щ проверяет код завершения (44.07) (команда grep возвращает нулевой код при совпадении строк с шаблоном). Вывод команды grep направляется в файл /dev/null аз.ну, некоторые версии этой команды имеют опцию s (silent — тихий), которая служит для выполнения того же действия. Этот сценарий предназначен для сетевых файловых систем, когда ваш начальный каталог монтируется на нескольких компьютерах. Если этот каталог не присутствует на различных компьютерах или если во всех системах используются одинаковые регистрационные сообщения, сценарий можно отредактировать, удалив переменную hostname и соответствующую команду. - JP 2.15 Однократный вывод регистрационного сообщения Если игнорировать длинные регистрационные сообщения, можно пропустить какую-то важную информацию. Рекомендуем организовать вывод регистрационного сообщения из файла /etc/motd только в том случае, если он изменился с того момента, когда вы его читали. Опция -t (/Ш) команды & сортирует список файлов, помещая в его начало файл, который был изменен последним. В следующем примере для хранения вывода команды Is -t, сравнивающей время изменения двух файлов, используются массивы интерпретатора csh (47.05). Если файл /etc/motd является более новьш, чем файл ~/.hushlogin, то выполняются следующие две команды. Поскольку эти команды используются в моем файле .login (2.02), они работают в любом сценарии. И set files=(~ls -t /etc/motd ~/ .hushlogin4) if ( Sfiles[l] == /etc/motd ) then cat /etc/motd csh init, touch -/.hushlogin thjiit endif unset files Примечание: Если вы создали псевдоним для команды (ю.ю) Is с целью обеспечения вывода всех данных (например, для вывода размеров файлов служит опция -s), кроме имен файлов, то в этом сценарии вместо Is нужно использовать системную версию /bin/Is. ВхоЯ в систему 59
2.16 Этот метод предполагает использование файлов .hushlogin, имеющихся во многих версиях UNIX: если этот файл существует, то процедура" входа в систему" происходит без сбоев. В нашем случае файл .hushlogin выполняет двойную функцию, т.к. он хранит временную метку, изменяемую при помощи команды touch (21.оц. Метод сравнения при помощи команды Is -1 будет работать в любой версии UNIX и всегда окажется полезным, если потребуется сравнить два файла. Такой же метод можно применять для изменения временной метки файла, когда произошло какое-либо событие, а также для сравнения каталогов. В этих целях используется команда Is -d (16.0s). - JP 2.16 Использование незарегистрировакного интерпретатора shell Начиная с версии 4.2 BSD, в берклиевских системах UNIX команда chsh (или подобная ей) может выбирать регистрационный shell только из списка, содержащегося в файле /etc/shells. Это можно рассматривать как элемент системы защиты, подобный необходимости ввода старого пароля перед указанием нового, поскольку в данном случае предотвращается предоставление вам другими пользователями необычного интерпретатора shell (в качестве шутки). Кроме того, это позволяет системному администратору предоставлять пользователям список достаточно надежных интерпретаторов. "Стандартными" обычно считаются Bourne shell и С shell. Если вы хотите использовать другой регистрационный интерпретатор shell и в вашей системе есть файл-/ей/5Йе//5, попросите своего системного администратора добавить в него новый интерпретатор, который должен • быть помещен в защищенный каталог-, чтобы взломщики системы не могли его испортить. Если системный администратор не захочет зарегистрировать ваш новый интерпретатор, прибегните к следующему способу: войдите в систему "с установленным по умолчанию | интерпретатором shell, а затем автоматически замените интерпретатор любым другими (подробнее — в параграфе 51.09), . 1. Если ваш регистрационный интерпретатор — не С shell, при помощи команды chsh или| подобной команды замените его интерпретатором С shell. , I 2. Если вашим новым интерпретатором должен стать bash, пропустите этот шаг. Иначе в! своем начальном'каталоге создайте жесткую или-символическую ссылку (is.04) на новый! интерпретатор shell. Используйте имя, в начале которого стоит минус. Это заставит! интерпретатор shell работать в качестве регистрационного (st.ov). Например, чтобы создать! в начальном каталоге символическую ссылку с именем -ksh на файл /usr/local/bin/ksh,im введите следующую команду: | ./23.14 % la -s /usr/local/bin/ksh ./-ksh 1 > - - I 3. Добавьте в начало файла xshrc (2.02) строки, заменяющие csh новым интерпретатором shell; (Заменить процесс позволяет команда exec (45.07).) — Если применяется Bourne shell, который при входе в систему читает файл .рюЩ используйте следующие строки: # Уничтожение исходного регистрационного С shell и замена- его на ksh. setenv SHELL /usr/local/bin/ksh ?ЫЩ5.)0 # Если установлена переменная $TERM (командами login или rlogin) , # запускается регистрационный shell. - su 22.22 # К сожалению, это заставляет команду su также использовать регистрационный shell if 47.03 lf"(S?TERM). then- . ' $? 47.04 Cd exec -ksh # символическая ссылка на регистрационный shell else exec $SHELL endif echo **-*****-*■ WARNING: exec ksh FAILED ******** 60 Ъсть первая. Работаем 1
2.16 Если вашим новым интерпретатором будет bash, замените строку exec -ksh следующей: exec $SHELL -login поскольку интерпретатор bash имеет опцию -login, которая сообщает ему, что нужно работать как регистрационный shell. Очень просто, не так ли? — Если ваш новый регистрационный интерпретатор shell подобен интерпретатору csh и читает файл .cshrc, необходимо добавить в файл .cshrc условную конструкцию, которая предотвращает образование бесконечного цикла. В этой условной конструкции в качестве флага применяется переменная среды (в.оу SH_EXECI>. # Уничтожение исходного регистрационного С shell и замена его на tcsh. if (! $?SH_EXECD) then setenv SH_EXECD yes setenv SHELL /usr/local/bin/tcsh # Если установлена переменная $TERM (командами login или rlogin)/ # запускается регистрационный shell. # Используйте оператор switch, а не if, поскольку csh содержит ошибку # в операторе else. # К сожалению, это заставляет команду su также использовать регистрационный # интерпретатор shell, switch ($?TERM) сазе 1: cd exec -tcsh # символическая ссылка на регистрационный интерпретатор shell default: exec $SHELL # запуск нерегистрационного shell breaksw endsw echo ******** WARNING: exec tcsh FAILED ******** • endif — С shell может не обнаружить ваш новый интерпретатор shell (-ksh или -tcsh), если не указать в последовательности поиска (6.07) свой начальный каталог (.) (укажите его в конце строки последовательности поиска). Можно также использовать полное путевое имя (14.02) нового интерпретатора shell, но тогда появляется вероятность того, что исчезнет начальный минус и shell не будет функционировать как регистрационный. — Может ли случиться, что новый интерпретатор shell однажды исчезнет? Например, уверены ли вы, что он находится в защищенной файловой системе? Неплохо дополнить код, приведенный выше, следующей условной конструкцией: -е 47.04 if (-e мой_ноъь1Й_эпе11) then кол для запуска нового интерпретатора shell... else echo **** WARNING: new shell failed. Using csh. *.*** 4. Проверьте новую конфигурацию: — Попробуйте выполнить команды, запускающие порожденный shell (зям), например su^ rsh й т.п. (2.07), и убедитесь, что в результате происходит запуск нового интерпретатора shell. — Поставьте в начале своего файла .cshrc команду set echo (#.П), чтобы убедиться, что команды этого файла выполняются. — При помощи команды £s (3S.os) вида ps $$ (в System V — ps -f -p $$) проследите за своим интерпретатором shell ($$ является идентификатором вашего интерпретатора shell (3S.03)). — Перед выходом из интерпретатора shell попробуйте войти в систему с другого терминала (2.щ, чтобы убедиться, что файлы инициализации нового интерпретатора shell работают. 'систему 61
5. Теперь можно работать. Если ваш регистрационный интерпретатор shell не записан в файл /etc/shells, программа ftp (52.07) (в действительности — демон (i.и) ftpd) может отказаться копировать файлы в вашу систему. Это происходит, в частности, из-за того, что программе ftp запрещено копировать данные в систему через специальные учетные записи, не имеющие паролей, а именно: sync, ■who, finger и т.д. Поскольку ваш интерпретатор shell не зарегистрирован, ваша учетная запись тоже относится к этой категории. Однако при использовании приемов, описанных выше, проблем не возникает. У вас есть зарегистрированный интерпретатор shell, записанный в файл /etc/passwd, поэтому программа ftp работает обычным образом. - JP
3 Выход из системы 3.01 Выполнение команд при выходе из системы Хотите ли вы, чтобы каждый выход из системы сопровождался неким действием: выполнением программы, удаляющей временные файлы, выдачей каких-либо вопросов или выводом приветствия? При использовании С shell для этого необходимо создать в начальном каталоге файл .logout (2.02) и поместить в него нужные команды. По завершении работы регистрационного интерпретатора С shell этот файл будет прочитан. Однако не все интерпретаторы shell являются регистрационными, а вам, возможно, понадобится, чтобы другие интерпретаторы также читали подобный файл при завершении своей работы. В параграфах 2.07 и 2.08 приведен ряд соответствующих рекомендаций. Вот что может содержаться в файле .logout. • Команда, подобная fortune (зщ, которая при выходе из системы выводит что-нибудь забавное. • Фоновая команда удаления временных файлов, подобная описанной в параграфе 3.04. • Команда, выводящая файл с напоминаниями, например, о домашнем задании. • Сценарий, информирующий вас о времени, потраченном на работу над различными проектами, что впоследствии позволит составить таблицу затрат рабочего времени. • Команда clear (22.щ для очистки экрана, избавляющая следующего пользователя от необходимости ознакомления с тем, чем вы занимались.* Она также предохраняет экраны от прожигания люминофоров, чем чревато высвечивание одних и тех же символов на экране. (Некоторые разновидности UNIX очищают экран перед выводом приглашения login:. Конечно, это ничего не дает пользователям, подключающимся через коммутатор (53.01) или другое коммутационное оборудование, поскольку такое подключение разрывается перед выводом следующего приглашения входа в систему.) Если вы подключаетесь к серверу через сеть при помощи медленного модема или коммутатора и не успеваете следить за результатами выполнения команд, содержащихся в файле .logout, попробуйте поставить в конце файла команду sleep 2 (ю.02). В результате интерпретатор shell сделает перед выходом из системы двухсекундную паузу, увеличив таким образом временной интервал для передачи выходных данных на ваш экран. - JP 3.02 Выполнение команд при выходе из Bourne shell и Korn shell С shell имеет файл конфигурации .logout (2.02). Команды в файле .logout выполняются при выходе из системы. Bourne shell и Кот shell не имеют подобного файла. Вот как можно восполнить этот пробел: * Некоторые терминалы и окна эмуляции терминалов имеют буфер для "обратной прокрутки" предыдущих экранных страниц. Команда dear обычно не очищает его. *«од на системы 63
3.03 1. В файл .profile добавьте следующую строку: trap44.12 trap '. ?HOME/.sh logout; exit' 0 . 44.23 — (В некоторых системах вместо $НОМЕ используется $LOGDIR.) 2. Создайте в своем начальном каталоге файл .shjogout. Поместите в него команды, которые должны выполняться при выходе из системы. Например: clear W440S if [ -f $HOME/tOdO. tomorrow ] Г -1.44.20 then echo "======= STUFF TO DO TOMORROW ===" cat $HOME/todo.tomorrow f i Команда trap будет выполнять сценарий .shjogout при выходе из интерпретатора shell. - JP 3.03 Электронные приветствия It's a damn poor mind that can only think of one way to spell a word. — Andrew Jackson Только полный дурак может думать, что слова всегда произносятся одинаково. — Эндрю Джексон Too much of good things is WONDERFUL. — Mae West Когда все хорошо — это прекрасно. — Мэй Уэст Democracy is a form of government that substitutes election by the incompetent many for appointment by the corrupt few. — G. B. Shaw Демократия является формой правления, при которой назначенные коррумпированным меньшинством заменяются выбранными некомпетентным большинством. — Дж. Б. Шоу Research is what I'm doing when I don't know what I'm doing. — Werner von Braun Когда я не знаю, что я делаю, то говорю, что занимаюсь исследованием. — Вернер фон Браун I do not feel obliged to believe that same God endowed us with sense, reason, and intellect had intended for us to forgo their use. — Galileo Я не верю, что тот же Господь, который наделил нас чувствами, мышлением и разумом, хотел, чтобы мы отказались от их использования. — Галилей Computers are useless; they can only give answers. — Picasso Компьютеры бесполезны; они могут выдавать только ответы. -- Пикассо Эти сообщения выведены программой/огй/ие, которая обычно находится в каталоге /usr/games. При каждом запуске этой программы выводится сообщение, подобное одному из приведенных выше. Большинство пользователей запускают программу fortune из своего файла .profile или .logout. Если в вашей последовательности поиска (s.07) отсутствует каталог /usr/games, используйте имя команды /usr/games/fortune. 64 Часть первая. Работаем ДбЩ
3.05 При каждом запуске программа fortune должна записывать сообщение в файл fortune.dat. На компьютерах с сетевыми файловыми системами этот файл может находиться в файловой системе, предназначенной только для чтения, чтобы другие пользователи не могли модифицировать его. Если вы получите сообщение об ошибке и в вашей.системе установлено разрешение на запуск программы rsh (1.33) без запроса пароля на удаленной машине, узнайте у своего системного администратора, на каком компьютере эта файловая система смонтирована с разрешением на запись. После этого введите следующую строку: % rsh имя_компьютера ./usr/games/fortune Некоторые версии команды используют другой способ слежения за выводимыми приветствиями. Для файлов таких команд не должно быть установлено разрешение на запись. - JP 3.04 Автоматическое удаление файлов Если при работе в системе создаются временные файлы (21.оз), удалить их позволят команды из файла .logout. Какую конкретно команду использовать для удаления временных файлов, зависит от способа их создания. Типичная запись в файле .logout выглядит следующим образом: ~ 14.11 (set nonomatch; cd -/temp && rm -f *)S Скобки указывают на то, что должен быть запущен порожденный shell (i3.07j, поэтому команда cd не изменит текущий рабочий каталог родительского интерпретатора shell. В С shell необходима команда set nonomatch as.04), в результате выполнения которой интерпретатор не выдает сообщения об ошибках, если в каталоге temp отсутствуют файлы, подлежащие уничтожению. В Bourne shell эту команду нужно опустить. Присутствие оператора && fuoy означает, что команда rm не будет выполняться, если выполнение команды ed не было успешным. Использование выражения cd -/temp вместо rm -/temp/* позволяет сократить строку аргументов рз.об) при необходимости удаления множества временных файлов. Если вы одновременно работаете в нескольких сеансах, будьте осторожны и не удалите временные файлы, используемые в других сеансах работы. Одним из средств предохранения от нежелательного удаления файлов является команда find (17.02), используемая для удаления только тех файлов, которые не изменялись за последний день: xMg>931 find -/temp -type f -interne +1 | xargs rm -f S - JP 3.05 Предотвращение случайного выхода из С shell Вероятно, вы оказывались в следующей ситуации: случайно нажав клавиши [Ctrl-d], вы обнаружили, что неожиданно вышли из системы. Если да, то вам должна быть знакома переменная shell ignoreeof. [Ctrl-d] является символом конца файла, поэтому когда shell его встречает, то решает, что никакого ввода больше не последует, и прекращает работу. Если вы находитесь в своем регистрационном интерпретаторе shell (si.w), то Неожиданно выйдете из него, а если в другом процессе, то могут произойти неприятности следующего плана: исчезнет окно или неожиданно изменится ваша среда, поскольку вы перешли из порожденного интерпретатора shell (зам) в родительский. При использовании С shell эту проблему можно решить путем установки системной переменной ignoreeof. set ignoreeof # предупреждение случайного выхода из системы (Большинство пользователей выполняют эту установку в своих файлах .cshrc и .login.) Теперь случайное нажатие клавиш [Ctrl-d] не приведет к прекращению работы интерпретатора shell. Вместо этого вы получите вежливое сообщение Use "logout4 to logout или Use 'exit" to leave csh (для выхода из системы используйте команду logout или exit). **ВД па системы 6§
зм В Korn shell и bash используйте команду set -о ignoreeof. Если вы применяете Bourne shell, то найдете решение проблемы в параграфе 3.06. Возможно, мы с вами схожи в том, что не используем описанные выше приемы. Я обнаружил, что сочетание клавиш [Ctrl-d] гораздо удобнее команд logout и exit. Но я понимаю, что мою позицию можно оспорить, и даже допускаю, что могу ошибиться при наборе команд. - ML 3.06 Предотвращение случайного выхода из Bourne shell Из Bourne shell можно очень легко и незаметно выйти, всего лишь нажав клавиши [Ctrl-d]. В С shell есть переменная ignoreeof tf.os), которая не позволит вам так просто выйти из системы. То же можно сказать о Кот shell и bash, в которых для этой цели используется команда set -о ignoreeof. Выход из Bourne shell необходимо подтверждать. При отрицательном ответе запускается новый экземпляр интерпретатора shell, заменяющий старый. Сначала создайте файл, подобный файлу .logout (З.02) в С shell, который будет читаться при выходе из Bourne shell. Сохраните также в переменной среды (n.ot) имя терминала, возвращаемое командой tty (З.оа), — оно понадобится вам позже: ТТУ="tt у'; expo rt TTY trap 44.:12 trap ' .. $HOME/sh_logOut; exit1 0 J^jj> (В вашей системе, возможно, используется переменная $L0GDIR, а не $НОМЕ.) Поместите в ЩР свой новый файл .shjogout следующие строки: [46.10] exec < 45.20 exec < $TTY echo "Do you really want to log out? \c" read ans как 44.06 case "$ans" in [Xy]*) ;; execMUB *) exec $HOME/bin/-sh ; ; -sh SIM esac В последней строке использован хитрый прием запуска нового регистрационного интерпретатора shell (5i.o9). Прежде чем прочесть файл .shjlogout, shell закрывает специальный файл tty (45.20), а команда exec < $TTY опять делает ваш терминал устройством для стандартного ввода. Если ваша система работает очень медленно, вы можете не получить сообщение в течение нескольких секунд, забыть, что оно должно появиться, и выйти из системы. Я установил, что в моей системе не существует такой проблемы. Однако вы можете заменить команду read ans программой типа grabchars (45.32), которая через определенный промежуток времени вводит вместо пользователя ответ по умолчанию. В некоторых версиях Bourne shell применяются другие приемы, а в некоторых никакие приемы не нужны. - JP 3.07 Прерывание сеанса при помощи утилиты screen Если ваша система поддерживает псевдотерминалы (pty) (4i.os), можно воспользоваться удобной утилитой screen (Ш9), которая позволяет запустить процесс (например, ин- r9) J терпретатор shell, в частности csh, sh и т.п., программу чтения новостей и т.д.), а затем в любой момент прервать все процессы и выйти из системы. Затем можно снова войти в систему с любого терминала и продолжить выполнение процессов. Кроме того, вы можете поручить утилите screen продолжить выполнение программ после вашего выхода из системы. Эта утилита позволяет открывать много окон даже не в среде X Window и переходить из одного окна в другое. Обычно выход из утилиты screen происходит, когда прекращается выполнение каждого интерпретатора shell в отдельности или при помощи команд [Ctrl-a] и [Ctrl-\] уничтожаются screen 66 Часть первая. Работаем дом1\
3.08 все сеансы одновременно. Если вы хотите прервать сеанс утилиты screen с возможностью его продолжения, воспользуйтесь командами [Ctrl-a] и [Ctrl-d]. Вместо сообщения screen is terminated появится сообщение о прерывании работы программы и приглашение: [detached] % Чтобы впоследствии продолжить сеанс, запустите утилиту screen с опцией -г. Тогда вы вернетесь в тот сеанс утилиты screen, в котором велась работа до прерывания. Остальные процессы будут работать по-прежнему. Например, если вы находились посредине редактируемого файла во время одного из сеансов утилиты screen, сеанс редактирования останется активным, и вы сможете продолжить работу с того же места. Все это здорово, поскольку позволяет не только постоянно поддерживать активность всех сеансов (даже если вы вышли из системы), но и продолжить их с других терминалов. Так, например, можно пойти домой, войти в систему с домашнего терминала и продолжить работу с того места, где она была приостановлена. - LM, JP 3.08 На каком терминале я работаю? Каждый раз при регистрации в системе вам назначается "устройство" fly (ЗШ), которое управляет вводом и выводом на терминал, в окно и т.д. Каждое такое "устройство" имеет имя. Если вы вошли в систему с нескольких терминалов и другие пользователи хотят общаться с вами при помощи программ write и talk (из), они должны знать, какое лу-устройство вы используете. Одновременно можно применять несколько таких устройств. Чтобы выяснить, какое устройство используется в данном окне, выполните команду tty. % tty /dev/tty07 Теперь вы можете сообщить другим пользователям, что необходимо набирать команду write ваше_имя_пользователя tty07. В некоторых системах используются различные виды терминалов: терминалы, подключенные через телефонную линию, терминалы, доступ к которым осуществляется через сетевые порты с помощью программ rlogjn и telnet (МЗ) и т.д. Вы или ваш системный администратор должны отыскать файл типа /etc/ttys, чтобы выяснить, какие терминальные устройства применяются и для чего. Эти сведения можно использовать для автоматизации процедуры входа в систему. Например, многие сетевые терминалы на нашем компьютере имеют такие имена, как /dev/ttypx и /dev/ttyqx, где х — буква или цифра. В моем файле .logout (з.ор есть условная конструкция, которая очищает экран и выводит прощальное сообщение (з.оз) на все терминалы, кроме сетевых. # Очистка экрана и вывод приветствия на несетевые терминалы: -9/6 if ("-tty" !~ /dev/tty[pq]*) then Г 47.04 dear fortune Hendif GNU-версия команды tty находится на компакт-диске. Щ - JP Выход из системы з* 67
4 Организация начального каталога 4.01 А это обязательно? Компьютеры и офисы имеют одну общую особенность. Эта особенность состоит в той легкости, с которой теряются документы. Обычно в моем офисе можно наблюдать следующую картину: громоздящиеся повсюду стопки бумаг, разбросанные журналы и визитные карточки. Как правило, мне удается найти нужный документ, но я бы солгал, если бы стал утверждать, что всегда могу найти ту статью, которую читал вчера. Если взглянуть на начальный каталог пользователя-новичка, можно увидеть примерно то же, что и у меня в офисе. Вы обнаружите громадное количество несвязанных друг с другом файлов с загадочными именами. Зато вам не удастся найти никаких подкаталогов, кроме тех, которые порекомендовал создать системный администратор и которым, возможно, так и не нашли применения. В начальном каталоге новичка могут храниться программы из разных проектов, личная почта, заметки о совещаниях, файлы данных, незаконченные документы, электронные таблицы, созданные в прошлом месяце и ныне забытые, и т.д. Помните, что компьютерная файловая система — тот же шкаф с документами. Не выйдет ничего путного, если все документы сбросить в шкаф, не сортируя их по темам и подтемам. Все это будет напоминать свалку. Документы требуют систематизации. В случае файлов эта задача решается путем создания подкаталогов (аналогов шкафов и ящиков для документов). Файловая система UNIX о.щ поможет содержать все материалы в полном порядке. В этой главе мы дадим несколько советов относительно систематизации электронных документов. Конечно, документы могут затеряться, даже если вы постоянно следите за их упорядочением. В параграфах 17.20 и 17.31 приведены сценарии, в которых используются команды find и grep для поиска затерявшихся файлов. - ML 402 Каталог bin для программ и сценариев Если вы компилируете лрограммы (52щ или пишете сценарии для интерпретатора shell (i.m), разместите их в одном каталоге, например в подкаталоге вашего начального каталога. Если этими программами хотят пользоваться несколько человек, выберите любой другой каталог, при условии, что у вас есть право на запись в него. Обычно таковым является каталог bin. Своему каталогу я дал название Jiin (i.is), чтобы не разупорядочивать списки, выводимые командой Is. 1. Чтобы создать каталог bin в начальном каталоге, наберите следующее: % cd % mkdlr bin 2. Поскольку ваш каталог предназначен для хранения программ, позаботьтесь о том, чтобы интерпретатор shell мог найти их там. Введите команду echo $PATH и найдите путевое имя этого каталога в последовательности поиска. Например, если ваш каталог называется ■- /v/walt/bin, то вы должны получить сообщение, содержащее следующую строку: % echo $PATH . . . :/u/walt/bin: . . . 68 Часть первая. Работаем цош
4.05 Если данный каталог отсутствует в последовательности поиска, добавьте его (Ш). 3. Наконец, если другие пользователи должны входить в данный каталог, при помощи команды вида chmod go+nc bin (22.07) предоставьте им право доступа. При добавлении в каталог bin новой программы и использовании интерпретатора С shell нужно выполнить команду rehash. Это связано с тем, что интерпретатор С shell просматривает каталоги в последовательности поиска не напрямую, а с помощью хэш-таблицы, что ускоряет поиск. - JP 4.03 Хранение невыполняемых сценариев Большинство пользователей UNIX помещают свои сценарии и откомпилированные двоичные программы в подкаталог bin (4.02) начального каталога. А как поступать со сценариями других видов, которые не являются выполняемыми, но полезны в работе? Например, я использую редактор sed (34.24) для решения многих сложных задач редактирования. Я мог бы запускать его непосредственно, но использую сценарий runsed (34.03), который ищет файл в текущем каталоге sedscr. Разнообразные сценарии для редактора sed я держу в подкаталоге sedlib начального каталога. При необходимости использовать эти сценарии совместно со сценарием runsed я копирую их или создаю ссылку на них (поз) в каталоге sedscr. - TOR. 4.04 Каталоги для хранения программ редактора Emacs Если вы применяете в работе одну из версий редактора Emacs (зг.01) (GNU. Emacs или любую альтернативную коммерческую версию), то, возможно, написали много удобных программ на языке LISP и используете их в процессе редактирования. Полезно создать для этих программ отдельный каталог, самое подходящее имя для которого (очевидно) — emacs. Обычно такой каталог находится в начальном каталоге. При работе с GNU Emacs в файл .emacs нужно поместить следующую строку; (setc load-path (append load-path • ("ваш_каталог_етасз") ') ) Эта строка указывает редактору Emacs, что он должен искать ваши программы в личном каталоге (аналогично использованию переменной PATH (6М) интерпретатора shell). - ML 4.05 Персональные каталоги Персональный каталог создают для хранения личных файлов: любовных писем, финансовых документов, жалоб на своего Начальника, неприличных анекдотов и прочего. Назвать его можно как угодно, например private. [Обычно я даю своим персональным каталогам такие имена, по которым невозможно определить их назначение. — JP] Раз вы создали персональный каталог, нужно установить для него код режима доступа (22.02), равный 700. Это значит, что применительно к этому каталогу вы являетесь единственным лицом, которое обладает правами на чтение, запись и просмотр списка файлов. Вот как это делается: % mkdir private % chmod 700 private В любой системе UNIX каждый, кому известен пароль пользователя root, может стать суперпользователем и читать любые файлы. Поэтому персональный каталог ни в коей мере не обеспечивает полной защиты, особенно если учесть, что во многих системах UNIX - ■■--.-.. большинство пользователей знают пароль пользователя root. Но в какой-то степени это предохраняет информацию от посторонних глаз. Для обеспечения настоящей защиты файл всегда можно закодировать (27.17). - ML Организация начального катапога ;89
4.06 4.06 Наименование файлов Вспомним еще раз шкаф для документов. Если бы документы в нем назывались письмо!, письмо2, письмоЗ и т.д., то ничего невозможно было бы найти. То же верно и в отношении компьютера. Вы должны взять за правило присваивать файлам выразительные имена. Система UNIX поддерживает очень длинные имена файлов. В некоторых системах длина имени ограничена 14 символами, но в большинстве допускается применение имен длиной до 256 символов. Я не могу объяснить вам, как сделать имена файлов выразительными. Могу лишь сказать, что вместо того, чтобы называть файл письма letter, лучше подобрать имя, отражающее содержание документа. В качестве имени письма можно выбрать имя получателя, конечно, если вы сможете с легкостью установить связь между johnjshmoe и тем, что "письмо посвящено динамике цен на золото" (хотя я думаю, что имя gold_price_trends_oct гораздо лучше имени john_shmoe). Брюс Барнетт (Bruce Bamett) высказал идею о том, что путем создания длинных имен можно сформировать простую "реляционную базу данных". Например, всю информацию о ценах на золото можно найти при помощи такой команды, как more *gold*price. Конечно, в этом случае вы не располагаете теми чудесными возможностями, которые предоставляет коммерческая СУБД. Но, возможно, они вам и не нужны? И тратить деньги на приобретение СУБД не стоит? Если вы — программист, имя каждого файла программы должно указывать на ее назначение. Так, программа, предназначенная для приведения матрицы к диагональному виду, может носить имя diag.mat.c, а программа, принимающая данные из кассовых аппаратов, — tellerJnput.с' Еще один способ обозначения файлов различных типов заключается в использовании суффиксов, или расширений имен файлов (i.i7). -ML 4.07 Создавайте больше каталогов! Создав достаточное количество каталогов, вы получите ряд преимуществ: • Во-первых, если начальный каталог хорошо организован, легче найти нужный файл. Представьте ряд немаркированных шкафов для документов: сотрудники вставляют папки куда угодно, лишь бы поместились. С тем же успехом можно было бы выбросить документы, поскольку, когда они вам понадобятся, вы ничего не сможете найти. • Во-вторых, в системе UNIX обращение к файлу осуществляется намного быстрее, если объем каталогов невелик. Оптимальный объем каталога составляет около 60 файлов. • В-третьих, каталоги являются важной частью системы защиты UNIX (22.01). Вы можете использовать каталоги для защиты файлов от несанкционированного доступа. Создавайте каталоги для документов каждого нового проекта и подкаталоги для каждой подтемы. В идеале начальный каталог не должен содержать ничего, кроме подкаталогов. Ознакомьтесь с нашими рекомендациями по формированию системы каталогов: • Если вы — программист, создавайте новый каталог для файлов каждого проекта. Например, создайте каталог src для исходных текстов, каталог doc или man — для документации, каталог obj — для объектных файлов, каталог rel — для текущей рабочей версии программы, каталог test — для тестовых файлов и результатов тестирования и т.д. Если проект включает много файлов, каталоги src и obj следует разбить на подкаталоги — по одному для каждой части проекта. • Неплохо поместить все личные файлы (отделив их от рабочих) в каталог, который можно защитить от любопытных глаз (см. параграф 4.05). * Teller — кассир. — Примеч. мрев. 70 Часть первая. Работаем доив
4.08 • Многие пользователи сохраняют свою почту (из) в одном каталоге (как правило, с именем Mail), который впоследствии разбивается на подкаталоги по темам. Я использую вариант этой схемы: храню почту общего характера в каталоге Mail, а корреспонденцию по отдельным проектам — вместе с файлами самого проекта. Например, моя почта, имеющая отношение к данной книге, хранится вместе с исходными текстами книги. Способы быстрого создания каталогов описаны в параграфе 4.08. - ML 4.08 Как ускорить создание каталогов? В параграфе 4.07 мы говорили о том, что нужно иметь больше каталогов. Опытные пользователи UNIX постоянно создают каталоги. Как это делается? Очень просто. Используйте команду mkdir в комбинации с именем нового каталога: % mkdir имя_каталога Эта команда создает каталог с указанным именем, который не обязательно должен находиться в текущем каталоге. Например: % cd /home/los/mikel % mkdir /src/books/power/articles/files Существует только два требования: • Каталог, в котором вы хотите создать подкаталог, должен существовать (в данном случае — /src/books/power/articles). • У вас должно быть право на запись в родительский каталог. И А что, если родительского каталога не существует? Предположим, что каталог /src/books создан, а каталоги power и articles — нет. Можно создать их "вручную" или путем добавления опции -р, имеющейся во многих версиях команды mkdir, а также в ее GNU-версии на mkdir прилагаемом компакт-диске: % mkdir -p /src/books/power/articles/files Опция -р указывает команде mkdir, что нужно создать все необходимые промежуточные каталоги. Так, приведенная выше команда создает три каталога: 1. /src/books/power 1. /src/books/power/articles 3. /src/books/power/articles/files [Если ваша версия команды mkdir не имеет опции -р, в интерпретаторе csh или bash можно использовать перечень ранее введенных команд (и.оэу. % mkdir /src/books/power % !!/articles mkdir /src/books/power/articles % !!/files mkdir -p /src/books/power/articles/files Это почти настолько же быстрый способ. — JF] При работе в System V для каталога можно установить режим защиты файлов. (По умолчанию режим защиты файлов устанавливается с помощью команды umask (22.04).) Чтобы сделать это, используйте опцию -т, например: % mkdir -m 755 /src/booka/power/artides/files Данная команда создает каталог с режимом доступа 755, который позволяет владельцу выполнять по отношению к каталогу любые операции. Обращаем ваше внимание на то, что код режима должен быть числом (см. параграф 22.01, в котором изложены основные понятия системы защиты файлов и каталогов). - ML Организация начального каталога 71
4M 4.09 Инициализация редактора vi при помощи файла .ехгс Команды и установки, которые должны выполняться при каждом запуске редактора vi или ех (30.02), можно сохранить в файле .ехгс начального каталога. С помощью редактора vi вы можете изменять файл .ехгс так же, как обычный текстовый файл. Если у вас пока нет файла .ехгс, создайте его посредством редактора vi. Введите в этот файл команды set, сЖ (зол) и map (З1.02), которые должны активизироваться при использовании редактора vi или ex. Файл .ехгс может иметь следующий вид: set nowrapscan wrapmargin=7 set sections=SeAhBhChDh nomesg map q :иЛМ:пЛМ '• "Чтобы поменять местами два слова, установите курсор в начале "первого слова, а затем наберите v: map v dwElp ab ORA O'Reilly &_ Associates, Inc. AM — это символ возврата каретки. Введите его, нажав fCTRL-vl и клавишу [RETLTRN1 (зш). Строки, начинающиеся с двойной кавычки ("), являются комментарием. Поскольку, прежде чем попасть в редактор vi, файл читается редактором ех,.команды в файле .ехгс не должны начинаться с двоеточия. Редактор vi читает не только файл .ехгс в вашем начальном каталоге, но и файл с таким же именем в текущем каталоге. Это позволяет установите опции, подходящие для каждого проекта (30.06). При отсутствии признаков работы файла .ехгс внимательно просмотрите сообщения об ошибках перед тем, как редактор vi очистит экран. Если не удается быстро прочитать сообщения,, вместо vi запустите редактор ex. Из него можно выйти при помощи команды q!: % «х No tail recursion :q! Приемы, описанные в параграфе 42.08, также окажутся полезными. — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 4.10 Нахождение всех версий команды при помощи команды whereiz Чтобы найти полное путевое имя команды, пользователи Кот shell могут воспользоваться командой whence, а пользователи bash — командой type. В других интерпретаторах shell применяется команда which (so.os). Указанные команды выводят только первый каталог с искомой командой из числа тех, что заданы в переменной PATH (6.04). Команды с тем же именем, находящиеся в других каталогах, стандартная команда which не покажет. (Эту функцию выполняет команда which, предоставляемая на компакт-диске, в сочетании с опцией -а. То же относится и к AasA-команде type -all.) А команда whereiz обнаружит следующее: % which grep /иэг/bin/grep % whereiz grep /usr/bin/grep /usr/5bin/grep В моей системе в каталоге /usr/bin находится Беркли-версия команды grep. Каталог /usr/5bin ' содержит версии для System V. Команда whereiz позволяет также узнать, есть ли в каталогах, J указанных в вашей последовательности поиска, и локальная, и системная версии одной и ^ той же команды. i Приведем соответствующий сценарий. Имя команды заканчивается буквой z потому, что во- многих версиях UNIX уже имеется команда whereis (so.as>. jllfrll1 ' II'JII" UJLJUU^- -L-l..1-! ■■-J..U 1 1ЦШ 111 L ll. и ■ !■ *■ 72 Часть первая. Работаем йшШ
4.W [whereiz] && 44.09 #! /bin/sh # КОМАНДА, ПРОВЕРЯЮЩАЯ НАЛИЧИЕ ИСПОЛНЯЕМЫХ ФАЙЛОВ. testx="test -x" # ЗАМЕНА ПУСТОГО ПОЛЯ В $РАТН ТОЧКОЙ (.) fixpath="'echo $PATH | sed \ ■ ■/я'. СИСТЕМНО-ЗАВИСИМАЯ: -е -е -е 's/' ■s/: "s/i :/: $/: ./■ IFS=" # УСТАНОВКА $IFS (ДВОЕТОЧИЕ, ПРОБЕЛ, ТАБУЛЯЦИЯ) # ДЛЯ АНАЛИЗА ПЕРЕМЕННОЙ $РАТН for command do where=n" * ОБНУЛЕНИЕ $where # ЕСЛИ КАТАЛОГ СОДЕРЖИТ ИСПОЛНЯЕМЫЙ ФАЙЛ, ДОБАВИТЬ ЕГО В СПИСОК for direc in $fixpath do $testx $direc/$coramand && where="$where $direc/$command" done case "$where" in ?*) echo $where ; esac # ЕСЛИ ЧТО-ТО СОДЕРЖИТ - ВЫВЕСТИ done Команда sed (34J4) "исправляет" переменную PATH. Она заменяет пустое имя каталога (:: — посредине строки PATH; : — в ее начале или конце), которым обозначен текущий каталог. Пустое имя заменяется относительным путевым именем, т.е. точкой р.гр, поэтому переменная shell direc не будет пустой. В строке 9 в кавычки заключены символы двоеточия, пробела и табуляции. Благодаря этому в переменную IFS (35.21) заносится разделитель полей (:), позволяющий разбивать последовательность поиска на отдельные имена каталогов в цикле for (44.16). Это полезный способ обработки списков с разделителями-нолей в виде двоеточия. - JP йрпннщия начального каталога П
5 Инициализация терминала 5.01 Информация о терминалах Перед вами — одна из трех глав, посвященных инициализации терминала. В ней приведена большая часть сведений, необходимых для настройки терминала посредством файлов конфигурации shell (2.02). В главе 41 терминалы рассматриваются более подробно. Кроме того, в этой главе раскрыты основные технические понятия и приведены специальные примеры. Глава 42 посвящена проблемам, связанным с терминалами. В ней даны советы, как решать такие проблемы. - TOR 5.02 Понятие базы цанных терминалов Изготовители предлагают терминалы различных типов, каждый из которых обладает определенным набором возможностей и соответствующей ценой. Есть терминалы новые и устаревшие, "умные" и "тупые", с большими и малыми экранами, текстовые и графические, а также терминалы со всевозможными специальными функциями. Различия между терминалами не имеют особого значения для таких программ, как сш (25.т) ■ и who (5I.04), которые используют их как печатную машинку с бесконечно длинным листом бумаги. Эти программы выдают последовательности символов и не прибегают к специальным: возможностям терминала; им не нужна информация о терминалах. Только такие программы,; как текстовые редакторы, использующие функции управления экраном, нуждаются в; сведениях о параметрах терминала. \ В конце 70-х Билл Джой (Bill Joy) создал в Беркли текстовый редактор vi (30.02). Как и все| экранные редакторы, редактор vi использует экран терминала в режиме произвольного доступа.:! Подобная программа должна не просто выводить символы один за другим, а манипулировать! текстом, выведенным ранее, прокручивать страницы, перемещать курсор, удалять строки,| вставлять символы и т.д. Если полное обновление экрана обеспечивается самим терминалом,! управление выводом на экран сильно упрощается. 1 Первые версии редактора vi написаны специально для терминалов ADM3a фирмы Lear Siegler.: Новый редактор имел настолько значительные преимущества перед строчными редакторами,* что возникла настоятельная необходимость его переноса на терминалы других типов. Проблема состояла в том, что терминалы обладали различными возможностями, а для управления одинаковыми возможностями применялись различные коды. Вместо того чтобы писать отдельные драйверы (42.oi) для терминала каждого типа, Билл Джой; предложил нечто оригинальное, что сегодня воспринимается пользователями UNIX как; данность. Чтобы избежать жесткого кодирования управляющих символов и параметров; каждого терминала, он создал версию редактора vi с типовыми командами управление экраном. ; 74 Часть первая. Работаем Я^Щ
5.03 Механизм типовых функций управления терминалом основывался на двух вещах: базе данных, описывающей возможности каждого поддерживаемого терминала, и библиотеке подпрограмм, позволяющих программам обращаться к этой базе данных и использовать функции и параметры, которые содержатся в базе. Библиотека и база данных получили название termcap, образованное из слов terminal capabilities (параметры терминала). Пользователи UNIX воспринимают как должное возможность применения с системой UNIX почти любого терминала и беспроблемную работу с такими программами, как экранный редактор w. На самом деле это было громадным шагом вперед. База данных termcap хранится в единственном текстовом файле, который с годами стал довольно объемным, включив в себя описания сотен различных терминалов. Для повышения производительности фирма AT&T предложила базу данных terminfo, в которой описания терминалов хранятся в сжатом виде и в отдельных файлах. Если программа предназначена для работы с базой данных termcap или terminfo, то для определения типа терминала она читает переменную среды TERM (s.io), а затем ищет в базе данных запись об этом терминале и переписывает в свои переменные значения параметров, которые будут применяться. Такие программы могут быть самыми разными — от экранных редакторов типа v/ и emacs (32.01), использующих все описания параметров, до программ типа clear (22.1S), которым требуется только один параметр (Escape-последовательность для очистки экрана). Подобными программами являются тоге (25.оз), pg, rogue, tset (5.03), ul и nroff (4з.и). — JS, из книги termcap & terminfo издательства O'Reilly & Associates 5.03 Установка типа терминала при входе в систему Если вы все время работаете с одним и тем же терминалом, его тип следует явно задать в файле .login (2.02): xtm/6.01 setenv TERM vtlOO или в файле .profile amy. export 6.01 TERM=vtlOO; export TERM Но если вам, как и многим пользователям UNIX, время от времени приходится входить в систему с разных терминалов, из дома или с различных систем в сети, необходим более гибкий способ установки типа терминала. Для этого в файлы конфигурации интерпретатора shell можно поместить различные условные конструкции (2.12). Кроме того, в большинстве случаев тип терминала можно определить с помощью команды tset, хотя она предназначена в первую очередь для инициализации терминала (s.ny. • Если не задано никаких аргументов (Ш) и переменная TERM установлена, для определения типа терминала команда tset использует значение этой переменной. • Если не задано никаких аргументов и переменная TERM не установлена, команда tset использует значение, указанное в системном файле /etc/ttytype или /etc/ttys (только в BSD 4.3 и в производных от нее системах). • Если тип терминала указан в качестве аргумента, то это значение используется вместо значения переменной TERM. • Опция -т позволяет точнее определить тип терминала, особенно если возможна неоднозначность. Например, если сегодня вы подключаетесь через телефонную линию, завтра — через локальную сеть, а послезавтра — по длинному кабелю, с помощью опции -т можно указать, какой именно тип входа в систему используется в данный момент, в соответствии с чем может быть установлен тип терминала. В Bourne shell команду tset можно применять для установки значения переменной TERM следующим образом: TERM=*tset - -Q опции') export TERM Иннцианизация терминала 75
ш (При наличии опции - команда tset направляет информацию о типе терминала на стандартный вывод (U.0I). В противном случае она инициализирует терминал (s.n) и не сообщает о его типе. Опция -Q (quiet — спокойный) подавляет вывод сообщения об установленных кодах удаления введенного символа и строки, которое обычно выдает команда tset при инициализации терминала. Обратные кавычки (9.16), в которые заключается команда tset, обеспечивают вставку результатов ее выполнения в командную строку.) Чтобы в С shell можно было использовать результаты выполнения команды tset, нужно выполнить команду eval (s.io). Это позволит установить значение переменной TERMCAP (5лн). (Нужно также ввести команду set noglob, как объясняется в параграфе 6.09.) Для ознакомления с возможностями команды tset рассмотрим случай, когда последовательный порт компьютера соединен с модемом, через который могут подключаться несколько пользователей, работающих на терминалах различных типов. В этом случае в файле /etc/ttytype следует установить тип терминала по умолчанию — diahip. Тогда для установки типа терминала каждого пользователя можно использовать в файле .login команду tset следующим образом: set noglob eval 'tset -s -Q -m 'dialup:vtlOO'' Это значит, что, если в файле ttytype указан тип терминала dialup, нужно использовать терминал vtlOO. Двоеточие разделяет значение из файла ttytype и значение, которое должно быть поставлено ему в соответствие. Если пользователь хочет также получить запрос на подтверждение типа терминала, следует поместить между двоеточием и типом терминала знак вопроса: set noglob ' eval 'tset -s -Q -m 'dialup:?vtlO0'' Тогда появится сообщение: TERM = (VtlOO) При нажатии клавиши [RETURN] будет использован указанный терминал: В противном случае можно ввести другой тип терминала. Вы можете указать команде tset запрашивать тип терминала без проверки общей записи типа dialup. Достаточно задать после опции -т требуемый тип терминала, поставив перед ним знак вопроса. Например: set noglob eval 'tset -s -Q -m '?vtl00'' Для разных скоростей передачи данных могут быть указаны различные типы терминала. Предположим, в домашних условиях вы используете при подключении терминал Wyse-50 с модемом на 9600 бод, а в дороге — переносной компьютер с эмулятором терминала VT100 и модемом на 2400 бод. В таком случае необходимо воспользоваться следующей командой tset. set noglob eval 'tset -s -Q -m 'dialup@2400:vtlOO' wy50' При условии, что в файле ttytype записан тип dialup, команда tset выберет тип терминала VT100, если скорость модема составляет 2400 бод, и тип терминала wy50, если скорость не. является таковой. [Следите за переключателями скоростей линии. Они не работают в большинстве сетевых систем: обычно скорость порта компьютера превышает скорость порта, терминала. Та же ■■■ проблема характерна сегодня для телефонных модемов, практикующих сжатие данных. — JP\ i Для определения скорости передачи используются различные символы: i\ @скорость Номинальная скорость i <скорость Больше номинальной скорости j >скорость . Меньше номинальной скорости. 76 Часть первая. Работаем доЩ
5.05 Чтобы изменить смысл оператора сравнения, следует поставить перед ним восклицательный знак. (Например, выражение !@1200 служит для обозначения любой скорости, кроме 1200 бод. В С shell для этого употребляется обратная косая черта: \ !@1200 (ii.oq.) При указании нескольких опций -т будет выбрана первая подходящая. Если ни одна опция не подходит, выбирается последнее значение, указанное в строке без опции -т (см. предыдущий пример). Если тип терминала не указан, используется тип, записанный в файле /etc/ttytype. Рассмотренные способы изменения типа терминала не всегда срабатывают. Причины этого описаны в параграфе 42.03. В параграфе 41.09 приведен сценарий для настройки терминала. — TOR, из книги termcap & terminfo издательства O'Reilly & Associates 5.04 Установка переменной TERMCAP с помощью команды tset f*i\]/ Пользователи С shell смогут обратиться к еще более мощной функции команды tset (s.o.y. Л±7^ Опция s заставляет ее посылать на стандартный вывод последовательность команд С shell ЩР для установки не только переменной TERM, но и переменной TERMCAP (в соответствии с [39.08] действительным содержимым записи в файле termcap).. Это ускоряет запуск программ, использующих файл termcap: им не нужно больше просматривать этот файл в поисках нужной записи — она под рукой. Вызов команды tset осуществляется следующим образом: set noglob eval*./» eval 'tset -Q -s друт"ие_ опции' Чтобы понять назначение команды tset, давайте посмотрим, что она выводит на экран, если ввести строку без оператора eval: % tset -Q -a «y50 set noglob; setenv TERM wy50; setenv TERMCAP 'n9Iwy50:li#24:co#80:am:bs:bw:ul:\ :cm=\E=%+\040%+\040:nd=AL:up=AK:do=AJ:ho=AA:bt=\EI:\ : cl=AZ: ce=\ET: cd=\EY: al=\EE: dl=\ER: ic=\EQ: dc=\EW: \ :so=\EG4:se=\EGO:sg#1:ue=\EGO:us=\EG8:ug#l\040: \ :me=\E(EGO:mb=\EG2:mp=\E):mh=\EGp:mr=\EG4:mk=\EGl:\ : kl»AH: kr=AL: ku=AK: kd=M: kh=AA : kl»AA@AM: k2=AAAAM: \ :k3=AABAM:k4=AACAM:k5=AADAM:k6=AAEAM:k7=AAFAM:k8 =AAGAM:\ :k9=AAHAM:кО=ЛА1ЛМ; unset noglob; (Формат файла termcap рассмотрен в параграфе 41.11.) Команда set noglob (6.09) заставляет shell приостановить интерпретацию спецсимволов, потому что это может вызвать проблемы. После выполнения команды восстанавливается исходное состояние интерпретатора shell. В параграфе 42.03 описана ситуация, когда такой способ установки не срабатывает. — TOR, из книги termcap & terminfo издательства O'Reilly & Associates 5.05 Запрос типа терминала: программа qterm Команда tset (5.оз) — мощный инструмент, необходимый при частом входе в систему с терминалов различных типов. С помощью этой команды можно задать тип терминала по умолчанию, а также установить новый тип терминала при входе в систему: TERM = (VT100) Пользователь, применяющий команду tset, должен знать тип своего терминала. Однако вы можете входить в систему с терминала, тип которого неизвестен. Кроме того, без вашего сведения терминал может быть сконфигурирован для эмуляции другого терминала. Пользователи-новички особенно будут в недоумении от сообщения команды tset. Инициализация терминала 77
5.05 В качестве альтернативного средства попробуйте поработать с программой Майкла Купера (Michael Cooper) qterm, предоставляемой на нашем компакт-диске. Эта программа посылает терминалу тестовую строку и на основе его ответа определяет тип терминала. Благодаря qterm программе qterm вы всегда можете правильно установить тип своего терминала, поместив в файл .login строку '...' 9.16 setenv TERM 'qterm' или в файл .profile строку TERM='qterm"; export TERM Достоинство программы qterm состоит в том, что она устанавливает тип терминала без вашего участия. Вам не нужно знать тип своего терминала — он устанавливается автоматически. Программа qterm конфигурируется с помощью списка ответов и соответствующих им типов терминалов. По умолчанию программа ищет этот список в системных каталогах, например в каталоге /usr/ucb/lib/qtermtab или /usr/local/lib/qtermtab. Если программу qterm вызвать с опцией +usrtab, она будет искать файл .qtermtab в вашем начальном каталоге. Для определения типа терминала обычно предназначена команда [ESC][Z]. Пример файла qtermtab, который распространяется вместе с программой qterm, содержит ответы некоторых терминалов на тестовую строку: # # QtermTab - Query terminal table for qterm # TermName FullTermName vtlOO Base vtlOO vtlOO vtlOO with STP vtlOO ANSI/vtlOO Clone h29 Zenith z29 in zenith mode vt52 generic vt52 vtlOO AT&T UNIX PC 7300 Если ваш терминал не указан в этом списке, его можно добавить. Чтобы определить ответ терминала на строку запроса, введите команду echo "A[Z". Например, я вошел в систему со своего домашнего эмулятора терминала на компьютере Macintosh и обнаружил, что программа qterm не распознала тип моего терминала: % qterm Terminal NOT recognized - defaults to "vtlOO". vtlOO Программа по умолчанию выбрала правильный тип терминала, но лучше ввести собственную запись. Я определил ответ моего терминала на ввод тестовой строки [ESC][Z]: echoAfli» % echo "A[Z" "[[E;Y| (Обратите внимание, что для ввода символа ESC следует набрать Л [.) В файл ответов терминала я добавил следующую строку: Л[г Л[[Е;У| vtlOO Macintosh terminal emulator Теперь при запуске программы qterm терминал распознается: % qterm Terminal recognized as vtlOO. (Macintosh terminal emulator) vtlOO #SendStr # "[Z *[Z *[Z *[Z A[Z "[Z Receives A[[?l;0c Л[[?1;1с л[[?1;2с л[/к л[/г A[[0n 78 Часть первая. Работаем дома
5.06 Строка Terminal recognized as... посылается только в стандартный поток ошибок (l.r.oi). а строка типа терминала — на стандартный вывод (u.oi). Поэтому при вводе командной строки % setenv TERM 'qterm4 Terminal recognized as vtlOO. (Macintosh terminal emulator) устанавливается правильное значение переменной TERM: % echo $TERM vtlOO Следует помнить, что точность работы программы gterm определяется файлом qtermlab. He все терминалы отвечают на команду [ESC][Z]. Кроме того, можно не найти такую команду, на которую терминал отвечает однозначно. Некоторые терминалы имитируют терминалы других типов. Например, сейчас я использую окно xterm (ui), но программа qterm считает, что я работаю на vtlOO: % echo $term xterm % qterm Terminal recognized as vtlOO. (ANSI/VT100 Clone) vtlOO Вы можете отредактировать свой файл .qtermtab, т.е. изменить его так, что ответ А[ [?1;2с будет соответствовать окну xterm: #A[Z Л[[?1;2с vtlOO ANSI/vtlOO Clone A[Z A[[?l;2c xterm xterm window а затем вызвать программу qterm с опцией +usrtab в командной строке: setenv TERM qterm +usrtab" - LM 5.06 Если терминал зависает при входе в систему... Если терминал зависает при входе в систему, поступите следующим образом: • Попросите опытного пользователя просмотреть ваши файлы конфигурации (2.02). В них могут быть ошибки, которых вы не замечаете. • Войдите в систему под другим именем и введите команду su ваше имя (22.22) при работе с Bourne shell или команду su -f stucklogin, если используется csh или tcsh. Войдите в начальный каталог (cd). Переименуйте файлы конфигурации так, чтобы интерпретатор shell не видел их во время входа в систему. Если после этого удастся войти в систему, значит, проблема была связана с файлами конфигурации, относящимися к данной учетной записи. • Переведите интерпретатор shell в режим отладки (S.n). Войдите в систему под другим именем или с помощью суперпользователя, запустите редактор и введите в начало файла конфигурации sh (например, .profile) приведенную ниже строку. Это позволит увидеть, какие команды файла .profile выполняются, а какие вызывают зависание. set -xv Все строки файла .profile и выполняемые команды отобразятся на экране. Если на экран ничего не будет выведено, значит, файл не читается. Пользователи С shell должны поместить следующую команду в начало файла .cshrc: set echo verbose Инициапиэация терминам 79
5.07 Обратите внимание, во многих системах UNIX интерпретатор shell не читает файлы конфигурации, которые не принадлежат вам. Чтобы проверить это, воспользуйтесь КОМаНДОЙ Is -I (22.02). • Просмотрите запись пользователя в файле /etc/passwd (Зб.оз). Проверьте, имеет ли она нужное количество полей, разделенных двоеточиями, и нет ли другого пользователя с таким же регистрационным именем. (Если в вашей системе есть команды v/pw(8) и pwck(&), системный администратор с их помощью должен отредактировать и проверить файл passwd. Указанные команды позволяют решать многие подобные проблемы.) • Используются ли в вашей учетной записи удаленные каталоги, смонтированные при помощи сетевой файловой системы (NFS) р.зз)? Если удаленный сервер или сеть недоступны, то любая команда в ваших файлах конфигурации (особенно set path) Может зависнуть при попытке доступа к таким каталогам. Чтобы решить эту проблему, зарегистрируйтесь под другим именем с помощью команды su, как было описано выше, и уберите команду или имя каталога из файла конфигурации. Если подобная проблема возникает часто, системный администратор может смонтировать сетевую файловую систему с опцией soft (вместо используемой по умолчанию опции hard) и ограничить количество попыток монтирования. • Как "зависание" можно воспринять ситуацию, когда сообщения на терминал (по самым неожиданным причинам) не выводятся вообще. Тогда команда set -xu вам не поможет. В подобном случае попробуйте вставить следующую строку в начало файла .profile: exec > /tmp/sh.out.$$ 2>Sl Когда Bourne shell начнет читать файл .profile, он создаст в каталоге /tmp (2Ш) файл sh.out.nnn, в который будет записывать выполняемые команды и сообщения интерпретатора shell. В С shell подобная команда отсутствует. - JP 5.07 Чем управляют и чем не управляют базы данных termcap и terminfo Важно знать, что многие профаммы вообще не используют базы данных termcap и terminfo и что существует ряд других механизмов, также способных влиять на работу терминала. Работой последовательного интерфейса управляют несколько системных файлов {etc/ttys и /etc/gettytab в BSD и других не АТ&Т-системах и etc/inittab и /etc/gettydefs в System V). Пользователи могут изменять параметры последовательного порта с помощью кбманды stty (5.09,41.оз, 41.02). Наряду с обычными коммуникационными параметрами (скорость передачи, количество стартовых битов и битов останова, контроль четности и т.д.) вы можете управлять: • преобразованием символов возврата каретки, генерируемых большинством терминалов, в символы перевода строки, ожидаемые большинством UNIX-программ; • разбивкой входных данных на строки; • установкой специальных управляющих кодов для удаления введенного символа, уничтожения строки и прерывания выполняющегося процесса. В базах данных termcap и terminfo нельзя установить позиции табуляции для терминала. Обычно это выполняется при помощи команды tabs. Дополнительную информацию можно найти в разделах stty(4) и termto(7) руководства по System V. Базы данных termcap и terminfo предназначены в первую очередь для управления визуальными атрибутами терминала. Заданные в них параметры терминала позволяют экранным профаммам определить, каковы размеры экрана (что необходимо для постраничной прокрутки и перемещения курсора), как поместить курсор в любую точку экрана, обновить экран, установить или отменить любой режим работы экрана (инверсное изображение, мигание, подчеркивание) и т.д. [Существуют другие способы установки размера экрана. См. параграф 42.05. — Щ 80 Часть первая. Работаем дот
5.09 Работоспособность терминала зависит не только от правильности установок в базах данных termcap и terminfo и в других системных файлах. Например, терминал может быть отключен из-за того, что какая-то программа оставила либо последовательный порт, либо-сам терминал в неопределенном состоянии. По этой причине при инициализации терминала (s.ii), которая выполняется программами tset и tput, происходит установка в исходное состояние как терминала, так и последовательного интерфейса. — TOR, из книги termcap & terminfo издательства O'Reilly & Associates 5.08 Терминальные Escape-последовательности Для управления работой большинства терминалов используются строки специальных символов — Escape-последовательности. Такие строки начинаются с символа ESCAPE (ASCII-код — 033 (51.03)). Этот символ вводится посредством клавиши [ESC], имеющейся почти на всех клавиатурах, а также при нажатии клавиши для ввода левой квадратной скобки в сочетании с клавишей [CONTROL] (часто обозначается следующим образом: А [). Кроме того, данный символ генерируется многими специальными клавишами. Например, клавиша [Т] позволяет генерировать Escape-последовательность типа А [ 0А. Такая последовательность дает команду терминалу передвинуть курсор на одну строку вверх. Специальные Escape-последовательности хранятся в записях о соответствующем терминале в базе данных termcap или terminfo (4i.n,s.<a), что позволяет различным профаммам правильно реагировать на нажатие специальных клавиш. Сами профаммы выдают Escape-последовательности для выполнения таких операций, как перемещение курсора по экрану, выделение текста и т.д. Иногда целесообразно создать Escape-последовательность вручную, с помощью псевдонима команды или сценария shell. Например, можно выделить знак приглашения рм) или написать команду, которая устанавливает для экрана терминала инверсный режим (41.09. В большинстве наших примеров используются Escape-последовательности для популярной серии терминалов VT100 фирмы DEC (которые также распознаются большинством программ эмуляции терминала). Как выяснить, какие Escape-последовательности используются вашим терминалом? Во-первых, эти последовательности должны быть перечислены в руководстве по эксплуатации терминала. Во-вторых, можно заглянуть в базу данных termcap или terminfo (s.10) и, используя руководство или такую книгу, как termcap & terminfo издательства O'Reilly & Associates, расшифровать используемый там загадочный язык. В-третьих, установить применяемые Escape-последовательности позволяют такие программы, как tcap и tput (41М). Чтобы ввести Escape-последовательности в файл, перед нажатием клавиши [ESC] следует активизировать в своем редакторе команду "переключения интерпретации следующего символа" (в редакторе w — ГСТКЬ-у] (ЗШ)). Как использовать Escape-последовательности в команде, описано в парафафе 41.09. Не смущайтесь, увидев Escape-последовательность следующего вида: A[[lm В некоторых терминалах в начале Escape-последовательности за символом ESCAPE следует настоящий символ левой квадратной скобки (А [). Хотя на экране они выглядят одинаково, на самом деле это разные символы (символ CTRL-[, или ESCAPE, отличается от [, так же как символ CTRL-C отличается от С). - TOR 5.09 Установка кодов удаления символа, удаления строки и прерывания Приходилось ли вам работать за терминалом, в котором клавиша удаления символов находится не там, где вы привыкли? Если да, то вы понимаете, насколько это дезориентирует! С помощью команды stt£ (4Ш) можно изменить код удаления символа (а также ряд других кодов), восстановив привычную установку. Инициализация терминала 81
Ш9 Команда stty воспринимает параметры двух видов. Чтобы ввести команду в интерактивном режиме, наберите stty erase клавиша, где клавиша — это клавиша, которую вы обычно используете для удаления символа ([BACKSPACE], [DELETE] и т.п.), и нажмите [RETURN]. Это срабатывает, если клавиша, указанная в команде, не выполняет другой функции. Если же данная клавиша где-либо задействована или команда stty помещена в файл .login или .profile, следует описать клавишу при помощи букв. Управляющие символы в файле .login допустимы, но нежелательны. Например, если клавиша [BACKSPACE] применяется в качестве клавиши удаления, добавьте следующую строку: stty erase Ah Чтобы использовать в этом качестве клавишу [DELETE], поставьте перед знаком вопроса обратную косую черту, чтобы интерпретатор shell не воспринимал его как специальный символ (1.16): stty erase Л\? Это означает, что команда stty позволяет представлять управляющие символы при помощи комбинаций следующего типа: Ах, где А — символ, вводимый соответствующей клавишей, а х — любой другой одиночный символ. Иногда перед символом х нужно поставить символ \, чтобы предотвратить интерпретацию х как специального символа shell [и символ \ перед символом А, чтобы Bourne shell не трактовал его как канал! — JP]. Конечно, вы не ограничены клавишами [BACKSPACE] и [DELETE] и можете выбрать любую другую клавиигу. Например, чтобы использовать в этом качестве клавишу [Z], введите команду stty erase Z. Правда, после этого вы не сможете ввести саму букву Z! В табл. 5.1 пепечислены функции клавиш, назначение которых позволяет изменять команда stty. Таблица 5.1. Функции клавиш, изменяемые командой stty Опция erase kill werase intr quit susp jrprnt Функция Удаляет предыдущий символ Удаляет строку Удаляет предыдущее слово Прекращает выполнение текущего задания Прекращает выполнение текущего задания, создает файл дампа Останавливает выполнение текущего задания (после чего можно перевести его в фоновый режим) Повторно выводит текущую строку Рекомендуемая установка А\? Аи Aw Ас А\\ Az Аг [DELETE] [CTRL-u] [CTRL-w] [CTRL-c] [CTRL-\] [CTRL-z] [CTRL-rl См. параграф 5.09 9.02 9.02 38.09 38.09 12.01 9.03 Команды stty everything (для BSD UNIX) и stty -a (для System V) выводят все текущие установки терминала. Опции werase и rprint не реализованы во многих версиях System V. Историческая справка: изначально для удаления символов предназначалась клавиша [#], а для удаления строки — клавиша [@]. Это восходит к доисторическим временам (4Ш), когда в качестве терминалов использовались обычные пишущие машинки. Замечу, что и в некоторых современных системах эти установки действуют по умолчанию. Примечание: Эмуляторы терминала, редакторы и другие программы могут изменять все установки терминала. По окончании работы они должны возвращать установки в исходное состояние, но это происходит не всегда. Поэтому не рассчитывайте, что ваши установки обязательно будут работать в эмуляторе терминала. Кроме того, эти установки не обязательно будут верными после выхода из эмулятора терминала. Программа tset пренебрегает назначениями клавиш (5.щ. Следовательно, в Файлах конфи; гурации (2.02) команду stty нужно помещать после команды tset. - ML 82 Часть первая. Работаем цоШ1
5.10 5.10 Какие терминалы можно использовать? Программа, использующая базу данных параметров терминалов, выбирает запись в соответствии со значением переменной среды TERM (6.oi). Эта переменная инициализируется при входе пользователя в систему. Вторая переменная, либо TERMCAP (5М), либо TERMINFO, также может быть установлена, если необходимо использовать параметры терминала, описанные не в стандартной базе. Определить, какой тип терминала система сделала текущим, просто. Введите следующее: $ echo $TERM Если ничего не будет выведено, значит, переменная TERM не установлена. (В С shell выводится сообщение TERM: Undefined variable.) Так же просто задать тип терминала. Обычно тип терминала определяется, когда пользователь входит в систему. Однако это можно выполнить и из командной строки: $ TERM=wy50; export TERM % eeteav TERM wy50 Как быть в ситуации, когда перед вами — незнакомый терминал и вы хотите установить его тип? Как узнать имя терминала, которое нужно использовать в качестве значения переменной TERM7 Имена терминалов, которые могут быть легально присвоены переменной TERM, можно определить, просмотрев файл /etc/termcap или список имен файлов в подкаталогах каталога /usr/lib/terminfo. Терминальная база данных termcap хранится в единственном файле — /etc/termcap. Это ASCII-файл: вся информация содержится в удобочитаемом виде. Каждая запись включает список имен данного терминала и его параметры. В первой строке каждой записи перечисляются различные имена, или псевдонимы, терминала. Приводятся и краткое, и полное имена терминала. При поиске можно ориентироваться на краткое имя. Например, если вы используете терминал Wyse-50 фирмы Wyse Technologies, то для проверки наличия соответствующей записи в файле termcap следует набрать: % grep tfyse /etc/termcap n9|wy50|Wyse Technology WY-50:\ При этом должна быть выведена одна или несколько строк, наподобие приведенной выше (если были обнаружены подходящие записи). В каждой строке приводится несколько имен терминала, разделенных вертикальной чертой (|). Второе имя (wy50) чаще всего используется в качестве значения переменной TERM. Скомпилированная база данных terminfo хранится в подкаталогах каталога /usr/lib/terminfo. Каждая запись скомпилирована программой tic и хранится в отдельном файле. Все имена терминалов, начинающиеся с буквы а, хранятся в каталоге /usr/lib/terminfo/'a (и так далее по алфавиту).* Ссылки оа.оз) построены таким образом, что описание терминала можно получить, используя одно из его имен. [Команда Is -R общ выводит весь список описаний. Такая команда, как find -name '*xxx*' -print (пм), находит описание параметров терминала определенной фирмы или модели ххх. —JP] Таким образом, в системе, поддерживающей terminfo, ту же запись (wy50) можно найти при помощи команды /у. $ le /uer/lib/terminfo/w wy-50 wylOO wy50 wyse-50 wyse50 * Исходный текст записей в некоторых системах содержится в файле /usr/lib/tenninfo.ti. Записи можно декомпилировать или вывести на экран с помощью программы infocmp. Инициализация терминала 83
5.11 В качестве значения переменной TERM нужно использовать имя соответствующего файла. Если из имени файла не совсем ясно, какую запись следует применять, то следует воспользоваться следующей командой для вывода длинного имени терминала: $ tput -Tname longname Например: $ tput -Twy50 longname Wyse Technologies Wy-50 Нужно знать, что для терминалов, имеющих конфигурируемые опции (например, опцию переключения в режим с шириной экрана в 80 и 132 символа), в базах данных tgrmcap и terminfo может быть несколько записей. Пока вы не разберетесь, как находить в терминальных базах данных нужную запись и чем различаются отдельные записи, вам придется руководствоваться интуицией, экспериментировать с каждой записью и определять, использование какой записи приводит к лучшему результату. В параграфе 41.11 более подробно описан формат записей в базах данных termcap и terminfo. [Если ни одна из записей не подходит, присвоение переменной ТЕЛМтакого имени, как dumb или unknown, обеспечит использование базовых установок, пока будут проверяться другие записи. При этом редактор vi функционирует в открытом режиме (зо.зв). Хотя такая программа постраничного вывода, как less fts.at), будет работать некорректно, результат окажется приемлемым. — JP\ — TOR, из книги termcap & terminfo издательства O'Reilly & Associates 5.11 Инициализация терминала при помощи команды tset При входе в систему, особенно в том случае, когда вы работаете на терминале не один, не помешает инициализировать его (установить в исходное состояние). Если в системе используется база данных termcap, активизируйте команду tset (5.оз), а если база данных terminfo — команду tpul (S.i2). Хотя с помощью команды tset можно задавать установки терминала, ее главное назначение — инициализация терминала. Данная команда выдает строку инициализации (если таковая определена в базе termcap в записи, относящейся к данному терминалу), которая должна перевести терминал в рабочий режим. В этом плане команда tset несколько совпадает с командой stt% (41.оз, s.09), связывающей команды удаления символа и строки с клавишами [CTRL-h] и [CTRL-x]. Различные опции позволяют назначить командам удаления другие клавиши (как и команде прерывания). После выполнения команды tset выводится следующее сообщение: Erase is control-H Kill is control-X (или же сообщение, содержащее другие комбинации клавиш, которые вы выбрали для этих команд). Вывод этого сообщения может быть подавлен путем добавления опции -Q. В некоторых системах встречается особая форма команды tset — reset. Наряду с выполнением., обычных функций команды tset команда reset устанавливает режимы терминала так, как, посчитает нужным (42.04,42.02). Таким образом, ее можно использовать для сброса параметров как последовательного порта, так и терминала в тех случаях, когда некорректная программа или неумелый пользователь оставит их в заблокированном состоянии. В. некоторых случаях перестает работать функция преобразования символа возврата каретки в символ перевода строки, чего, как правило, UNIX требует от терминалов. Тогда, чтобы , преобразование выполнялось, можно ввести: < |CTRL-j| reset |CTRL-j| i — TOR, из книги termcap & terminfo издательства O'Reilly & Associates ] ■4 84' '"" Часть первая. Работаем Д0Щ
5.12 5.12 Инициализация терминала при помощи команды tput Команда tput, которая используется вместе с базой данных terminfo, в некоторых отношениях эквивалентна команде tset (S.03,s.io, но, в отличие от нее, не может определять тип терминала. С другой стороны, она позволяет выбирать определенные параметры терминала и выводить их на экран или сохранять в переменных shell. [Команда tcap (4i.io) выполняет те же операции с базой данных termcap. — JP\ Благодаря этому сценарии shell используют такие возможности терминала (41.ю), как инверсное изображение и подчеркивание. По умолчанию команда tput предполагает, что применяется терминал, указанный в переменной TERM (S.io). Если вы хотите отменить действие этой переменной, укажите другой тип терминала при помощи опции -Т. Например: $ tput -Twy50 ... В System V Release 3 команда tput имеет опцию с ключевым словом, которая дает возможность восстановить исходное состояние установок терминала путем вывода строк инициализации, содержащихся в базе данных terminfo: $ tput init Команда $ tput reset посылает на терминал управляющую последовательность для сброса терминала, определенную в базе terminfo. Если такая последовательность не задана в базе, выводятся строки инициализации. Тогда команда tput reset работает точно так же, как команда tput init. В более ранних версиях System V эти ключевые слова не поддерживались, и нужно.было давать несколько команд tput, чтобы вывести каждую строку инициализации или сброеа по имени. Следующий сценарий, написанный Тони Хансеном (Tony Hansen) из AT&T, выполняет это автоматически: ^^^ #!/bin/sh ^^^Ч # Определение значения параметра iprog ■ ® 1 eval 'tput iprog" Е^Я # Вывод строк инициализации isl и is2 tputinit tput isl tput is2 # Если терминал поддерживает табуляцию, установить ширину символа табуляции, # иначе отключить этот символ if [ -п "'tput ht'" ] then stty tabs; tabs -8 else stty -tabs fi # При наличии файла инициализации вывести его содержимое •i2S.it) cat -s ""tput if" # Вывод строки инициализации is3 tput is3 Описание различных инициализируемых в данном сценарии параметров можно найти системном руководстве или в книге termcap & terminfo издательства O'Reilly & Associates. — TOR, из книги termcap & terminfo издательства O'Reilly & Associates Инициализация терминала 85
6 Переменные среды и shell 6.01 Цля чего нужны переменные среды? Многие утилиты UNIX, в том числе и shell, для реализации своих функций нуждаются в сведениях о вас и о том, что вы делаете. Что это за сведения? Во-первых, многие программы (особенно редакторы) должны знать, с каким терминалом вы работаете. Интерпретатору shell необходимо указать, где искать те или иные используемые вами команды (или программы). Есть программы (например, почтовые), содержащие команды запуска редактора как порожденного процесса. Таким программам следует сообщать имя этого редактора. Конечно, можно написать программы, которые требовали бы от вас помещения всей этой информации в командную строку. Например, вам пришлось бы ввести строку следующего типа: % mail -editor vi -term aaxdvaxk4B -favoritecolor blue_no_red Как правило, в течение длительного времени работа ведется с одним и тем же редактором. Чаще происходит смена терминалов, но не за период с момента входа в систему до момента выхода из нее. Кроме того, вам вряд ли захочется вводить подобную строку при необходимости отправить почту. Вместо того чтобы принуждать вас вводить в каждой командной строке полный объем информации, которая довольно редко изменяется, UNIX использует для хранения этих данных переменные среды. Например, переменная среды TERM (S.io) сообщает программам, терминал какого типа используется. Любая программа, которой необходимо знать это, может прочитать значение переменной TERM, определить тип терминала и вести работу соответствующим образом. Каталоги, в которых хранятся нужные вам программы, перечислены в переменной PATH (6.04>. При вводе команды интерпретатор shell в поиске соответствующей программы просматривает все каталоги, указанные в переменной PATH. Конечно, когда все программы хранятся в одном каталоге, переменная PATH не нужна. Но рано или поздно вы начнете писать собственные программы и сохранять их в каталоге для пользовательских программ (4.02). Тогда и понадобится сообщить интерпретатору shell, как отыскать (а.от) эти команды. Переменными среды управляет интерпретатор shell. Разница между ними и обычными переменными shell (б.щ состоит в том, что последние являются локальными для данного экземпляра shell (например, для сценария), а переменные среды "наследуются" каждой £*£}/. запускаемой программой, в том числе другим интерпретатором shell (знм). Это означает, что Ш^!^- новый процесс получает свои копии переменных среды, которые он может читать, изменять ЩР и передавать дочернему процессу. Вообще любой процесс в UNIX (а не только shell) передает [6.02J свои переменные среды дочерним процессам. Для установки системных переменных предназначены следующие команды: % eetenv ИМЯ значение С shell ; а.05 $ ЯМЯ«зяа«внже; export ИМЯ Bourne или Korn shell 8$ Часть первая. Работаем доШ
6.02 Параметр ИМЯ не имеет особого значения. Переменные среды можно создавать с каким угодно именем. Конечно, вам не придется подбирать имена всех переменных. Такие переменные, как PATH и TERM, имеют особое значение, поэтому во многих программах выполняется соглашение (б.оз) относительно уникальности их имен. Если же вы хотите создать переменную, хранящую, например, имя любимого человека, то можете сделать это следующим образом: % eetenv LOVER Judy При желании можно написать программу с именем valentine (день Св. Валентина), которая читает переменную среды LOVER и создает соответствующее сообщение. Если вы любитель коротких знакомств или забываете имена, то эта программа может быть даже полезной. Существует соглашение о том, что все имена переменных среды должны состоять из прописных букв. При создании собственных переменных этого соглашения можно не придерживаться и использовать литеры любого регистра. Тем не менее, нарушать соглашение также не имеет смысла. Имена переменных среды в стандартных программах UNIX всегда состоят из строчных букв. [Обычно я присваиваю своим переменным имена, состоящие из прописных литер, чтобы они выделялись. — JP] Если вы хотите отменить определение переменной среды в С shell, выполните команду unsetenv ИМЯ. (В некоторых версиях Bourne shell существует идентичная команда — unset ИМЯ.) Чтобы получить список всех переменных среды, воспользуйтесь командой printenv или env. ® ] (Обе находятся на компакт-диске.) Команда printenv позволяет также запросить конкретную переменную. Вот ее обычный отчет: % printenv EDITOR EDITOR^/usr/ local /bin/emacs % printenv HOME-/home/los/mikel SHELWbin/csh TERM-SUn USER-mikel PATH»/usr/local/bin:/usr/ucb:/bin:/usr/bin:.:/home/los/mikel/bin LOGNAME-mikel PWD=/home/los/mikel/power/articles PRINTER=-ps EDITOR^/usr/local/bin/emacs Команда set (й.ов) выводит подобный список переменных shell. Для определения значения какой-либо переменной можно воспользоваться командой echo (sm), указав в качестве аргумента имя, предваренное символом доллара (который сообщает интерпретатору shell, что имя переменной нужно заменить ее значением): % echo $TERH xterm - ML eav 6.02 Отцы и дети Нет, речь пойдет не о психологии отношений. Мы хотим напомнить вам об одной важной вещи. Рассматривая переменные среды (б.оо, мы отмечали, что каждый процесс получает копии переменных родительской среды. Если вы усвоите это правило, то избежите типичных ошибок. Рано или поздно все создают сценарий, который собирает какую-то информацию, устанавливает ряд переменных среды и завершается. После этого пользователь удивляется, почему не осталось и следа от "новых" переменных среды. Все очень просто. Процесс (залз) в UNIX не может изменить переменные родительской среды. Он получает копии переменных среды, изменения которых в родительском процессе не видны. Измененные Переменные процесс может передать своим дочерним процессам, но не в обратном направлении. (Яйцо не может учить курицу. :-)) - ML среды и shell 87
6.03 6.03 Стандартные переменные среды Мы указывали, что переменные среды применяются для хранения редко изменяемой информации и что есть множество стандартных переменных среды, используемых, многими программами UNIX. Такие переменные называются стандартными не потому, что их значения являются стандартными, а потому, что их имена и способы использования предопределены. Ниже перечислены важнейшие стандартные переменные: • PATH (6.04): содержит последовательность поиска команды (я.07> — список каталогов, которые shell посматривает при поиске команды. Эта переменная обычно устанавливается в одном из файлов конфигурации (2М). • EDITOR, может содержать имя вашего любимого редактора. Обычно устанавливается в одном из файлов конфигурации. Некоторые программы проводят различие между переменной EDITOR, в которой обычно указывается строчный редактор (зз.о/) (например, ed), и переменной VISUAL, содержащей имя экранного редактора (например, W). Многие помещают в эти переменные имя одного и того же редактора. (Korn shell последовательно читает переменные VISUAL и EDITOR чтобы определить режим редактирования командной строки аиз).) • PRINTER (43му. может содержать имя принтера по умолчанию. Эта переменная весьма полезна на сервере с несколькими принтерами: не нужно указывать программе lj)r (4.Ш), какой принтер использовать. Устанавливается в одном из файлов конфигурации. • PWD: содержит полное путевое имя текущего каталога. В некоторых интерпретаторах shell эту переменную автоматически устанавливает команда cd. Значение переменной PWD может быть проигнорировано (14. в) при переходе в каталог, являющийся символической ссылкой. • НОМЕ (i4.il) (во многих системах — LOGDIR): содержит полное путевое имя вашего начального каталога. Устанавливается автоматически при входе в систему. • SHELL: содержит полное путевое имя интерпретатора shell, который используется после входа в систему. Устанавливается автоматически при входе в систему. • USER или LOGNAME: содержит имя пользователя. Устанавливается автоматически при входе в систему и не изменяется. • TERM (s.ioy. содержит тип терминала, выбранный в базе данных termcap или terminfo. Обычно устанавливается в файле конфигурации shell. • TERMCAP (s.ioy. может содержать полную запись из базы данных termcap, соответствующую используемому терминалу. Ускоряет запуск некоторых программ. Не является необходимой. При определенных условиях устанавливается командой tset, которая обычно выполняется из файла конфигурации shell. • ENV: содержит имя файла инициализации, который должен выполняться при запуске Korn shell (см. параграф 2.02). Применяется только в этом интерпретаторе. • PAGER, может содержать имя вашей любимой программы постраничного вывода на экран, например тоге (25.03) или jess (2S.04). (Такая программа, как man (so.oi), использует переменную PAGER для определения программы постраничного вывода, применяемой, когда выходные данные не помещаются на одном экране.) • EXINIT (30.3S, 6.10/. содержит опции конфигурации редактора vi (а также редактора ех, от которого эта переменная получила свое название). • PS1: содержит первичный знак приглашения командной строки Bourne shell. С shell не хранит знак приглашения в переменной среды. Он использует переменную prompt, поскольку файл xshrc (2.02) читается при запуске каждого экземпляра этого интерпретатора (см. параграф 7.02). • PS2 (9.1зу. содержит вторичный знак приглашения (используется в каждой новой строке длинных команд типа while и for) Bourne shell. • MANPATH (so.ю): если ваша команда man (so.oi) поддерживает эту переменную, то переменная включает список разделенных двоеточиями каталогов, в которых содержатся страницы диалогового руководства. 88 Часть первая. Работаем flflW'J
6.04 • TZ (6.06/. содержит ссылку на информацию о часовом поясе. Это имя файла, который находится в каталоге /usr/lib/zoneinfo и в котором хранится информация о вашем местном часовом поясе. Читается такой программой, как date (Si.io, б.от). • DISPLAY, применяется в X Window (ui) для идентификации сервера дисплея (программы управления клавиатурой и экраном), который будет использоваться для ввода-вывода в Х-приложении. Поскольку, в отличие от С shell, интерпретаторы Bourne shell не проводят четкого разграничения между переменными среды и переменными shell, описанные переменные могут отсутствовать в других списках. Мы полагали, что переменные среды имеют относительно постоянные значения (например, содержат имя вашего любимого редактора). Но это не всегда так. В частности, в многооконной среде значение текущей высоты окна может храниться в переменной среды, которая изменяется вместе с размерами окна. Но, к счастью, остается верным утверждение о том, что переменные среды содержат информацию, которая изменяется не слишком часто. - ML 6.04 Переменная среды PATH Из всех переменных среды PATHн TERM(s./o) являются самыми важными. Другие переменные также нужны, но эти две могут испортить вам жизнь, если будут неправильно установлены. Переменная PATH представляет собой список каталогов, разделенных двоеточиями. Shell просматривает эти каталоги при поиске определенной команды. Поэтому, если вы хотите выполнять команды из подкаталогов /bin, /usr/bin и /usr/local текущего каталога и вашего персонального каталога bin, поместите в свой файл .login строку, подобную приведенной ниже. Пустая запись (: в начале и конце или :: посредине) служит для обозначения текущего каталога. $HOME/bin«flZ setenv PATH /bin:/usr/bin:/usr/local::$HOME/bin В параграфе 8.07 приведен дополнительный материал по установке переменной PATH. Самой типичной проблемой, связанной с переменной PATH, является ее случайное удаление. Обычно это происходит, когда вы неправильно изменяете ее значение. Если переменная А4Г# удалена, shell может выполнять только свои встроенные команды д.н», а также команды, для которых указано полное путевое имя. Приведем пример: % setenv PATH Случайное стирание переменной РАТИ % Is Is: Command not found Да, это может выбить из колеи, особенно если вы не понимаете, что произошло. Существует несколько способов решения описанной проблемы. Наиболее простой — выход из системы и повторный вход в нее. (Команда logout является встроенной командой С shell, поэтому при ее поиске не должно возникнуть проблем. Если появится сообщение об ошибке типа Not login shell, попробуйте выйти посредством команды exit.) Можно также прочесть (44.23) тот файл инициализации, в котором определяется переменная PATH (обычно — файл .login в С shell и файл .profile в Bourne shell): % source -/.login $ . $HOME/.profile В большинстве случаев это позволит восстановить часть последовательности поиска. Проблема в том, что большинство файлов инициализации добавляют несколько персональных каталогов к длинной исходной системной последовательности. В таком случае сначала просмотрите файлы инициализации системы (если они есть). Их путевые имена могут быть разными: % source /usr/lib/Cshrc % source /usr/lib/Login % source -/.login ^Ременные среды и shell 89
5.05 Другая проблема с переменной PATH проявляется в том, что пользователю не удается выполнить нужную команду. Это чаще всего случается, когда пишется новый сценарий с тем же именем, что и у стандартной команды UNIX (например, true). При попытке ее выполнения происходит только следующее: % true После долгой возни со сценарием пользователь приходит вроде бы к правильному выводу о том, что сценарий в порядке, а все дело в последовательности поиска. Переменная РАТИ может выглядеть следующим образом: % printenv PATH /bin : /usr/local:/usr/ucb:/usr/bin::/home/mkl/bin Shell просматривает переменную PATH слева направо, следовательно, он обнаружит стандартную системную команду true до того, как увидит новую. И эта новая команда не имеет шансов быть выполненной. Данную проблему можно было бы решить, указав в начале строки текущий каталог и каталог $HOME/bin. Тогда команды в текущем каталоге и в вашем персональном каталоге bin заменят стандартные команды. Однако делать это не рекомендуется, поскольку ухудшается защита файлов. Но что же предпринять? Выход один: при написании сценариев давайте им имена, отличающиеся от имен стандартных утилит UNIX (44.21). Если же действительно необходимо употребить совпадающие имена, используйте относительное путевое имя (Ui), чтобы указать на "программу true, которая находится в текущем каталоге": % ./true Определить местоположение команды в каталогах, указанных в переменной PATH, позволяют команды which (so.os), fmdcmd (н.щ и whereiz (4.щ. В параграфе 6.05 описана переменная С shell path. - ML 6.05 Переменные PATH и path Применительно к С shell утверждение о том, что переменная PATH содержит последовательность поиска команд, звучит несколько некорректно. На самом деле все сложнее. Переменная среды PATH используется для установки переменной shell path: как только при помощи команды setenv (6М) устанавливается переменная PATH, С shell изменяет переменную path соответствующим образом. Например: setenv PATH /bin:/usr/bin:/usr/local::$HOME/bin В переменной PATH пустая запись (::) служит для обозначения текущего каталога. Именно переменная С shell (e.os, б.оя) path содержит последовательность поиска команды. Ее синтаксис иной: список каталогов заключен в скобки (47.05), а каталоги разделены пробелами. Например: ~ 14.11 set path- (/bin /usr/bin /usr/local . -/bin) Если вы установите переменную path, то С shell автоматически установит переменную среды PATH. Обе переменные устанавливать не нужно. Многие вместо setenv PATH используют команду set path. - ML 6.06 Переменная среды 11 Хотя переменную TZ знают немногие, это полезная переменная. Она сообщает системе UNIX, в каком часовом поясе вы находитесь. Часовой пояс, используемый по умолчанию, устанавливается при инсталляции системы (предполагаем, это было сделано правильно). Во многих случаях требуется его изменение. Например, вы можете быть подключены при помоши коммуникационной программы к системе UNIX, находящейся в другом часовом поясе. Кроме 90 Час» первая. Работает до№\
6.07 того, возможно перемещение вашей системы в другое место. При этом нужно изменить часовой пояс, однако повторная инсталляция программного обеспечения нежелательна и даже опасна. В параграфе 6.07 показано, как при помощи переменной TZ определить время в другом поясе. Чтобы установить переменную TZ, используйте команду следующего вида: % setenv TZ часовой_лояс С shell $ ?г=часовой_пояс; export TZ Bourne shell Эти установки начинают действовать сразу после ввода команды. При вводе команды date (si.io) отображаются дата и время, характерные для вашего нового часового пояса. В качестве параметра часовой_пояс указывается имя файла в каталоге /usr/lib/zoneinfo или в его подкаталогах. Загляните туда, и вы увидите, насколько богатое содержимое у этих каталогов: часовые пояса США, Канады, Австралии, большей части Европы, Азии, Африки и Южной Америки. Присутствует множество "странных" часовых поясов: например, часовой пояс штата Индиана, в котором во многих местах соблюдают только летнее время, и часовой пояс штата Мичиган, включенный по причинам, совершенно для меня не понятным. Предположим, нужно установить время, соответствующее часовому поясу штата Вайоминг. Смотрим в каталог /usr/lib/zoneinfo и находим подкаталог US. В этом подкаталоге есть файл Mountain. Следовательно, название часового пояса должно выглядеть следующим образом: US/Mountain. Изменить часовой пояс и проверить результат позволяют следующие команды: % setenv TZ U6/Mountain % date Wed Mar 6 19:34:53 MST 1996 He беспокойтесь о разнице между летним и зимним временем. Хотя в разных точках мира эти правила не одинаковы, информация о них занесена в базу данных zonefiles. - ML 6.07 Который час в Японии? Н Переменная среды TZ (б.м) особенно удобна, если вы ведете дела с людьми в разных точках мира. Предположим, вы собираетесь позвонить торговому партнеру в Японию, чтобы узнать курсы акций. При этом необходимо знать, открылась ли биржа или, по крайней мере, tin проснулся ли ваш партнер! Определить время в той или иной точке мира позволяет сценарий tm. Можно также присвоить переменной TZ значение Japan, вывести дату и время и восстановить старое значение. Ниже приведены примеры изменения часового пояса, в том числе временной установки (t.io> значения переменной TZ в Bourne shell и С shell: % date Tue Mar 4 20:48:58 EST 1997 % tm Japan Wed Mar 5 10:48:07 JST 1997 % (setenv TZ Japan; date) Wed Mar 5 10:48:13 JST 1997 $ TZ=Japan date Wed Mar 5 10:48:22 JST 1997 Итак, в Японии — 10 часов утра. Удобное время для звонка! Аргументом переменной TZ является имя файла в каталоге /usr/lib/zoneinfo. Одна из ошибок, возникающих при работе с файлами этого каталога, связана с употреблением прописной буквы в начале имени файла. Установив для переменной TZ значение japan, мы получим значение времени по Гринвичу, которое» отличается от правильного всего на девять часов! Сценарий tm решает эту проблему, выводя сообщения, если не находит указанного файла. Еще одна проблема состоит в установлении правильного часового пояса для разных частей света. Например, без хорошего знания географии могут возникнуть затруднения при определении того, что правильным значением переменной TZ для Сиднея в Австралии 0 13.07 .-8.05 ЩШинм среды и shell 91
6.08 является Australia/NSW (New South Wales — Новый Южный Уэльс), а для Перта — Australia/West. Во многих системах файлы часовых поясов содержат сведения для крупных городов. Если запустить сценарий tm без указания часового пояса, он выведет список имен имеющихся файлов часовых поясов. - ML, JP 6.08 Переменные shell Переменные shell являются частным случаем переменных среды (б.во. Если вы — программист, помните, что shell — это интерпретатор языка программирования. Переменные shell принадлежат ему. Их можно устанавливать, выводить на экран и применять в работе так же, как в программе на языке С (а также FORTRAN или BASIC). В противном случае представьте эти переменные как ячейки, в которых сохраняется необходимая для работы интерпретатора shell информация. Если вы прочитали параграф, посвященный переменным среды, то обратили внимание, что мы определили их примерно тем же образом. Чем же переменные shell отличаются от переменных среды? При запуске нового интерпретатора shell или программы они наследуют все переменные родительской среды. Однако переменные shell не наследуются. Профаммист может представить переменные среды как глобальные, а переменные shell — как локальные. Существует соглашение, согласно которому переменные shell набираются строчными буквами. Так же как некоторые профаммы используют переменные среды, shell использует свои -переменные. Например, С shell использует переменную history (u.oi) для определения количества запоминаемых команд пользователя. Если определена переменная noclobber (и.щ, С shell предупреждает повреждение файлов, к которому могут привести ваши ошибки при работе со стандартным выводом. Большинство пользователей для определения этих важных переменных вставляют соответствующий код в свой файл .cshrc. Для установки переменной shell применяется одна из следующих команд: % set имя=зяачаияв С shell $ имя=значание Bourne shell Если опустить значение, переменная shell примет значение null. Например, правильными являются следующие команды: % set ими С shell $ ■№■ Bourne shell Внимание! Присвоение переменной значения null — это не то же самое, что ее удаление. Некоторые профаммы проверяют наличие переменных. Этим профаммам все равно, какое значение имеют переменные, пусть даже значение null. Для удаления переменной воспользуйтесь командой unset. К сожалению, в устаревших версиях Bourne shell эта команда отсутствует: % unset имя С shell $ unset ямя Bourne shell Список всех переменных среды выводит команда printenv (Berkeley UNIX) или. env (System V).' Для получения-списка переменных Bourne shell или С shell достаточно ввести командуй. Вот типичный результат ее выполнения в С shell: % set argv () cwd /home/los/mikel/power/articles history 40 home /home/los/mikel noclobber path (/home/los/mikel/bin /usr/local/bin /usr/ucb /bin /usr/bin .) prompt los% shell /bin/csh status 0 term sun user mikel * Команды printenv и env являются внешними (i.ia) и работают с любым интерпретатором shell. 92 Часть первая. Работаем 0
6.09 Вывести значение одной переменной позволяет следующая команда: % echo "$яют_перемвяной" (Несмотря на знак приглашения С shell, команда работает во всех интерпретаторах shell UNIX.) Чтобы использовать значение переменной shell, перед именем переменной нужно поставить знак доллара ($). Этот знак не нужен, если переменной присваивается новое значение. Имя можно заключить в фигурные скобки, например: ${имя}. Это делает код более понятным при написании сценариев. Фигурные скобки применяются главным образом для отделения имени переменной от текста. Впрочем, этот разговор уводит нас от темы использования переменных в интерактивном режиме и вовлекает в программирование (44.01). - ML 6.09 Специальные переменные С shell [Приносим извинения за отсутствие параграфа, посвященного переменным bash и tcsh. В книге рассматриваются "основные" интерпретаторы shell — sh и csh. Переменные csh работают в tcsh, а многие (в несколько другом виде) — ив bash. Их полный перечень можно найти в руководствах по bash и tcsh. — JP] С shell распознает и использует переменные среды, но он также применяет в своей работе множество простых переменных shell («.#*). Нет нужды превращать их в переменные среды для того, чтобы передавать в порожденные интерпретаторы shell (З8.04), поскольку каждый экземпляр С shell всегда читает файл .cshrc р.щ. Таким образом, устанавливаемые в этом файле переменные доступны во всех версиях С shell. Многие специальные переменные С shell используются как флаги: им не нужно присваивать определенные значения. Shell просто проверяет их наличие. Такие переменные устанавливаются командой set имя а не командой типа set имя=значение Ниже приведен перечень специальных переменных, используемых в С ,shell: • Переменная cdpath (н.аз) содержит список каталогов. Для перехода в подкаталоги перечисленных в ней каталогов достаточно указать в команде cd имя подкаталога. • Если установлена переменная echo (8.17), перед выполнением команды интерпретатор shell выводит командную строку, в которой выполнена подстановка всех метасимволов, включая подстановку команд из перечня (п.от). (Это удобно при отладке сценариев, например файла .cshrc.) Если установлена переменная verbose (8.17), интерпретатор shell выводит командную строку с командами, подставленными из перечня (И.о?), но без подстановки метасимволов в именах файлов. Опции Bourne shell -v и -х (4t.oi) работают по принципу переменных verbose и echo. • Если установлена переменная filec или complete, интерпретатор shell выполняет дополнение имен файлов (я.ов). Если же установлена переменная ftgnore (я.оя), . не выполняется дополнение имен файлов, заканчивающихся определенными символами, например .о. • Переменная cwd (14.13) содержит полное путевое имя текущего каталога. Ее устанавливают команды cd, pushd и popd. • Переменная hardpaths (ШЗ) служит для исправления ошибок в значении переменной cwd при переходе в каталог, являющийся символической ссылкой. • Переменная histchars (n.is) используется для замены таких символов, как знак восклицания (!) и знак вставки (А), при обработке перечня введенных ранее команд. • В переменной history oi.oi) указывается количество командных строк, которые нужно хранить в памяти, а в переменной savehist (ii.ii) — которые нужно сохранить в файле при выходе из *Р»адюые среды n shell 93
6.10 системы. Список сохраняется в файле .history, находящемся в начальном каталоге. При следующем входе в систему команды из списка становятся доступными для выполнения. • Если установлена переменная ignoreeof (3.os), интерпретатор shell не реагирует на ввод символа конца файла ([CTRL-d]) и при выходе из системы требует ввести команду logout или exit (38.04). Это предупреждает случайный выход из интерпретатора shell или из системы. • При помощи переменной mail (21.08) интерпретатор shell сообщает о наличии свежей электронной почты (].зз). • Переменная noclobber рз.об) позволяет избежать перезаписи файлов из-за переадресации вывода (>). • Переменная noglob отменяет обработку специальных символов (is.oy (см. параграф^ 5.04). • Установите переменную nonomatch, если хотите, чтобы С shell реагировал на отсутствие совпадения с шаблоном так же, как и Bourne shell psM). • Переменная notify (12м) заставляет интерпретатор shell выдавать сообщения о завершении или остановке фонового процесса. • Список каталогов, которые интерпретатор shell просматривает при поиске команды, содержится в переменной path (6.05). • Ваше регистрационное имя, хранящееся в переменной среды USER или LOGNAME (б.о.ц, хранится также и в переменной С shell user. • Знак приглашения в командной строке интерпретатора shell устанавливает переменная prompt (7.02). (В Bourne shell ей соответствует переменная PSI (Ш). Кроме того, Bourne shell позволяет установить вторичный знак приглашения (9.U), для чего предназначена переменная PS2.) • Код завершения (44.07) последней команды содержится в переменной status интерпретатора csh и ? интерпретатора sh. • Если на выполнение задания тратится больше времени, чем определено переменной time (39.03), интерпретатор csh выводит статистику для данного задания. - JP, TOR 6.10 Выполнение команд во временно измененной среде Для конфигурирования многих команд UNIX применяются переменные среды. Например, редактор vi читает команды инициализации из переменной среды EXJNIT. Иногда требуется изменить установку системной переменной на время выполнения только одной команды. Для этого существует более простой способ, чем изменение значения переменной и возврат к предыдущему ее значению после выполнения команды. • В Bourne shell введите: $ ИМЯ_ПЕРЕМЕНИОЙ-значвняе команда аргумента • В системах UNIX, в которых есть команда env, введите в С shell: % env ИМЯ_ПЕРЕМЕНЯОЙ=значение команда аргументы • В С shell практикуется также запуск интерпретатора shell в порожденном процессе оз.оп), например: % (setenv ИМЯ_ПЕРЕМЕВВОЙ значение; команда аргумент) Если переменная EXJNIT содержит строку set wrapscan showmatch number и вы хотите только однажды добавить опцию nowrapscan в конец этой строки, введите следующую команду (в Bourne shell): $ 6.01 $ EXINIT="$EXINIT nowrapscan" vi afile По завершении работы редактора vi переменная EXINIT не будет содержать параметра nowrapscan. Прекрасный пример использования описанного метода приведен в параграфе 6.07. - JP 94 Часть первая. Работаем дмй|
7 Установка строки приглашения 7.01 Зачем нужно изменять строку приглашения? Во многих системах исходным символом приглашения С shell является символ процента. Не слишком информативен, не так ли? Он только сообщает, что вы работаете в системе. Если вы в состоянии запомнить имя своего текущего каталога, имя компьютера, текущее регистрационное имя и т.п., а также если вы надолго не покидаете свой терминал, то, возможно, этого символа достаточно. Но я все это забываю. Я вхожу в систему с разных терминалов и часто отвлекаюсь. Без дополнительной информации в приглашении мне пришлось бы все время выяснять, где я нахожусь, набирая команды pwd или who am i. Я изменил строку приглашения так, чтобы она содержала максимум необходимой мне информации. Хотя на ее основании я не могу установить все, что хотелось бы (по крайней мере, в С shell), она значительно упрощает задачу. Кроме того, игра со строкой приглашения может быть забавной. Это одна из самых популярных игр в UNIX, особенно среди новичков. Данная глава поможет вам освоить приемы работы со строкой приглашения. В первых параграфах изложены основы. В остальных описаны различные строки приглашения и способы их создания. Экспериментируйте. Выберите то, что вам больше подходит. - JP 7.02 Основные понятия Строка приглашения в С shell содержится в переменной shell (6.es) prompt, а в Bourne shell — в переменной PS1. Значение самой переменной устанавливается обычным образом. [В bash и tcsk эти две переменные имеют много дополнительных возможностей. Примеры приведены в следующих параграфах. — JP] Если я хочу изменить строку приглашения так, чтобы она включала мое регистрационное имя, я должен ввести в файл xshrc следующую команду: set prompt="tim % " (Удобно оставить в конце приглашения символ %, указывающий на С shell. Пробел после этого символа позволяет образовать интервал между строкой приглашения и введенной командой.) В приглашении будет присутствовать имя системы, в которую мы вошли, если команда имеет следующий вид: '...'9.16 set prompt="'uname -n' % " чпипе -n S0.07 Чтобы отображался номер каждой вводимой команды в перечне ранее введенных команд (П.01), включите такую строку: set prompt="\!% " Уяановка строки пригпашения 95
7M Для демонстрации всех трех параметров введите команду set prompt="tim@"uname -n~ \!% " Последняя команда обеспечивает вывод следующего приглашения: timgisla 43% - TOR 7.03 Проблемы со строкой приглашения в программах vi, rsh и т.д. [Нестандартные символы в строке приглашения могут привести к неправильному выполнению многих команд, запускаемых из неинтерактивного интерпретатора shell. Возможно, эта проблема решена в вашем интерпретаторе С shell. Однако замечание относительно ускорения выполнения файла .cshrc, высказанное Крисом Тереком (СТ), остается в силе. — JP] Если установить строку приглашения в файле .cshrc без предварительной проверки ее наличия (2.09), многие версии С shell начнут добросовестно выводить ее в канал, который редактор vi использует для подстановки специальных символов [символов подстановки в именах файлов (*, ?, П) (1.щ и тильды (~) (i4.ii) — JP]. Когда вы набираете : г abc*, редактор vi открывает канал к С shell и выводит в него команду echo abc*, а затем читает ответ. Если ответ содержит символы пробела или новой строки, редактор сбивается. Строка приглашения в файле .cshrc может быть установлена в виде (п) [в качестве приглашения в скобках выводится номер команды в перечне — TOR]. Тогда вместо abc.file редактор получит от интерпретатора shell следующую строку: (1) abc.file (2) Для решения проблемы следует исправить файл .cshrc <2М) следующим образом: Х47.03 if ($?prompt) then $?prompt 47.04 1 . # команды, выполняемые в интерактивном режиме, например: set prompt=! (\!) ' endif Это срабатывает, потому что неинтерактивный интерпретатор shell не имеет исходной строки приглашения, а в интерактивном она имеет следующий вид: %. Когда файл' .cshrc имеет большой объем, выполнение программ, запускаемых другими программами посредством строки csh -с ' команда', можно существенно ускорить, поместив в условную конструкцию все команды, которые должны выполняться в интерактивном режиме. — СТ, из телеконференции net.unix-wizards в Usenet, 22 апреля 1984 г. 7.04 Быстрая установка строки приглашения с помощью встроенных команд Для установки строки приглашения выполняется определенная команда (в большинстве интерпретаторов она устанавливает переменную shell). Вы могли выполнять другие команды (например, команду определения имени текущего каталога) перед установкой приглашения, собирая необходимую для этого информацию. Shell способен выполнять команды двух видов: встроенные и внешние (l.io). Обычно выполнение встроенных команд происходит быстрее, чем внешних. Разница особенно заметна на "медленных" компьютерах. Ожидание приглашения в течение нескольких секунд может раздражать. Выручит вас творческое использование встроенных команд. Рассмотрим некоторые примеры: 1. Команда pwd — это внешняя команда, которая просматривает файловую систему (1404), чтобы определить имя текущего каталога. (В некоторых системах эта команда является встроенной. В этом случае просмотр файловой системы не осуществляется.) Некоторые интерпретаторы shell хранят имя текущего каталога в переменной (обычно в cwd или 96 ' ■- 7-*-.| Часть первая. Работаем доШ'
7.04 PWD). На компьютерах с низкой производительностью первая из приведенных ниже команд выполняется медленнее: •...'9.16 set prompt=" 'pwcT% " set prompt="$(cwd}% " Здесь есть один подводный камень: встроенная команда не всегда (и.щ дает правильный ответ. Кроме того, в С shell при каждом изменении каталога нужно выполнять новую команду set prompt. Для автоматизации этой процедуры можно использовать пользовательскую команду типа setprompt (zos). 2. Возможно, вы захотите поместить в строку приглашения только конечную часть имени текущего каталога (следующую за последней косой чертой). Как отредактировать путевое имя? Многие вспоминают о команде basename (4S.18) или редакторе sed_ (34.24). Используя имя текущего каталога из $cwd, они могут набрать: set prompt="'basename $cwd"% " To же самое, но гораздо быстрее, выполняется с помощью встроенного в С shell оператора : t (tail — хвост): [){.08 set prompt="${cwd:t)% " Если текущим является каталог /usr/users/hanna/projects, оба приглашения будут выглядеть следующим образом (с пробелом после символа процента): projects% С shell имеет несколько встроенных операторов для обработки строк, подобных : t. Kom shell и bash обладают более мощными операторами обработки строк (9.вт). 3. Kom shell и bash могут включать в свое приглашение текущее значение другой переменной shell. Вставьте значение переменной PWD (в.вз) в строку приглашения, т.е. в переменную PS1, и в ней всегда будет фигурировать текущий каталог. Можно вставить значение любой другой переменной. При ее изменении будет меняться и приглашение. Следует учитывать такой нюанс: в строке приглашения нужно использовать одинарные кавычки, а не двойные, чтобы переменные, содержащиеся в строке, не обрабатывались (s.i4, s.osj до вывода приглашения на экран. Поместим в строку приглашения имя текущего каталога и значение переменной PSX. При изменении любого аргумента изменится само приглашение: $ PSX=foo $ PS1='$PWD $PSX\$ ' /home/jerry foo$ PSX=bar /home/jerry bar$ cd .. /home' bar$ cd . . 4. Интерпретаторы tcsh и bash также обладают возможностями настройки строки приглашения, позволяющими включать в нее имена сервера и пользователя, значение времени и т.д. При использовании этих интерпретаторов можно не применять внешние или пользовательские команды типа setprompt для установки строки приглашения. Например, чтобы приглашение содержало имя текущего каталога, символ новой строки (для перехода в следующую строку двухстрочного приглашения (z.os)), значение времени и, наконец, символ $ или %, выполните следующие команды: PSl='$PWD\n\t \$ ' bash set prompt = '%~\\ tcsh %р%% • Дополнительную информацию вы найдете в книгах Using csh & tcsh и Learning the bash Shell издательства O'Reilly & Associates, а также в руководстве по вашему shell. Итак, ускорить установку приглашения можно за счет применения встроенных команд. В параграфе 7.11 мы продемонстрируем, как использовать в приглашении команду dirs. - JP Установка строки приглашения 4 9-171 97
Многострочные приглашения Многие пользователи отдают предпочтение информативным приглашениям, содержащим имена сервера и каталога, номер команды в перечне, имя пользователя и т.п. Некоторые сокращают приглашения настолько, чтобы в длину они занимали не более одного экрана и еще осталось место для ввода команд: <elaineq@applefarm> [/usr/elaineq/projects/april/week4] 23 % Is Даже если приглашение имеет небольшой объем, прочитать его после выполнения нескольких команд бывает весьма сложно (терминалы не выделяют ввод пользователя полужирным шрифтом, поэтому и я не сделал этого): <elaineq@applefarm> [~] 56% cd beta <elaineq@applefarm> [-/beta] 57% which prog /usr/tst/applefarm/bin/beta/prog <elaineq@applefarm> [-/beta] 58% prog 61,102 units inventoried; 3142 to do <elaineq@applefarm> [-/beta] 59% Интересным решением этой проблемы является создание приглашения, состоящего из нескольких строк. Ниже приведен фрагмент файла .cshrc, в соответствии с которым формируется приглашение из трех строк: пустой строки, строки с именами сервера и текущего каталога, а также строки с номером команды в перечне и символом процента: uname-п 50.07 set hostname='uname -n" alias setprompt 'set prompt="\\ (...) 6.0S $(hostname} :$(cwd)\\ \! % *" alias cd 'chdir \!* && setprompt' setprompt # для установки начального приглашения Приглашения выглядят следующим образом: applefarm: /usr/elaineq/projects/april/week4 23 % prog | tee /dev/tty I mail -s "prog results" bigboss@corpoffice 61,102 units inventoried; 3142 to do applefarm: /usr/elaineq/projects/april/week4 24 % cd -/beta applefarm: /usr/elaineq/beta 25 % prog | mail Joanne Пустые строки служат для разделения команд, хотя их можно и удалить, сэкономив таким образом место. В качестве альтернативы Майк Сиерра (Mike Sierra), сотрудник издательства O'Reilly & Associates, использует цепочку звездочек: ( ***** 23 *** mike@mymac> *** /home/mike/calendar ***** % cd September г ***** 24 *** mike@mvmac> *** /home/mike/calendar/September ***** % В bash для вставки в приглашение символа новой строки не нужна специальная команда установки многострочного приглашения. Достаточно разместить в начале новой строки приглашения символ \п (символ новой строки). Многострочные приглашения хороши тем, что дают много информации и оставляют достаточно места для ввода команд. Конечно, вы можете выбрать для приглашения у информационное наполнение, отличное от предложенного мною. Главное, что я хотел донести г до вас — идея целесообразности применения многострочных приглашений. -JP 98 Часть первая. Работаем ДомЩ 7.05 И eshjnit thjnit
7.07 7.06 Информация о сеансе в строке состояния терминала Некоторые пользователи не любят указывать в приглашении имена текущего каталога, сервера и т.д., поскольку это загромождает экран. Полезную информацию можно разместить в строке состояния или заголовка, если таковыми обладает терминал или многооконная система. Этот способ хорош тем, что информация видна и во время выполнения программы. Правда при его использовании информация устаревает, если ее не обновлять при переходе в новый сервер или каталог. При выполнении пользовательской команды cd выполняется также команда echo, выводящая Escape-последовательности (s.os) для управления строкой состояния терминала или окна. Ниже приведен код пользовательской команды cd и другие команды для файла xshrc. Если на сервере www.jpeec.com войти в каталог /home/jpeec, в строке состояния будет выведено: www:/home/jpeec Формат строки состояния можно изменить. Измените командную строку $ {host: h}: $ {cwd} так, чтобы выводилась необходимая вам информация. set e=""echo -n x I trx \\033~" ^Создание символа ESCape set host='uname -n" # Вывод $host и $cwd в строку состояния терминала VT102. В нем используются # следующие Escape-последовательности: # ${е}7 = запоминание позиции курсора, $(e}[25;lf = переход в начало строки # состояния (строка 25), ${е)[0К = очистка строки, $(е)8 = восстановление # позиции курсора alias cd 'chdir \\* SS \\ echo -n "$<e}7$<e}[25;lf$<e}[OK $(host:h}:$(cwd)$(e)8"' Если вы постоянно используете терминал VT102 (а это делают многие), данная команда будет хорошо работать. При работе с другим терминалом нужно прочитать соответствующее руководство либо запись в базе данных termcap или terminfo (4i.ii) и найти его Escape-последовательности. Когда работа ведется на терминалах нескольких типов, которые несовместимы с VT102, в сценарий можно добавить конструкцию case (44.os> или switch (47.06) для определения типа терминала и активизации пользовательской команды cd, написанной для данного терминала. (Кроме того, эта команда помещает информацию о состоянии в строку приглашения на тех терминалах, которые не имеют строки состояния.) В этом случае могут возникнуть трудности: если команда определяется в файле xshrc, а терминал инициализируется в файле .login, то тип терминала может быть не установлен до тех пор, пока не будет определена команда. Из этого положения есть выход (2.07). Содержимое строки состояния может также утратить актуальность при удаленном входе в систему (1.12), запуске порожденного интерпретатора shell (З8.м) и т.д. При этом в строке состояния наряду с новым может выводиться старое сообщение. Чтобы предотвратить это, надлежит ввести указанную ниже команду для перехода в текущий каталог (.) и сбросить строку состояния: % cd . - JP Ш Приглашение с подсказками для начинающих Некоторые пользователи не желают видеть символ приглашения % или $. Если вы применяете всего несколько команд UNIX, то можете ввести их в строку приглашения shell. Вот простой пример установки приглашения Bourne shell в файле .profile: PSl='Type "rn", "mailx", "wp", or "logout": ' echo...033 4S.3S В cshjait shjtiit &&. 44.09 Установка строки приглашения 4* 99
7.08 А это — установка многострочного приглашения (7.05) С shell в файле xskrc: ($?prompt) 2.09 if ($?prompt) then set prompt='\\ Type "rn" to read news,\\ type "mailx" to read and send mail,\\ type "wp" for word processing, or\\ type "logout" to log out.W YES, MASTER? ' endif Предлагаем вам пофантазировать. - JP рму 7.08 Выделение символов в приглашении csb_mH shjnit echo...033 uname -n Используя шрифтовые способы (например, увеличение размера символов), можно выделить определенную информацию в приглашении или все приглашение на фоне остального текста на экране. Если терминал имеет специальную Escape-последовательность (S.os> для увеличения размера шрифта (что характерно для большинства терминалов), ею можно воспользоваться для выделения целого приглашения или его фрагмента. Предположим, мы хотим обратить внимание пользователя на то, что он зарегистрирован как пользователь root (суперпользователь). Для этого следует выделить в приглашении слово root. Сделаем его мигающим. Вот фрагмент файла .cshrc для пользователя root # Поместить символ ESCAPE в переменную $е. Для включения режима мигания на # терминале VT100 используется последовательность ($(е)[5т), а для возврата # в обычный режим - (${е)[0т): 45.35 set e="'echo x I trx \\033"" 50.07 set prompt="$(e} [5mroot$(e} [Om@"uname -n'# " Приглашение может выглядеть следующим образом (на экране слово root мигает): root@sys.ora.com# Строка установки приглашения заключена в двойные кавычки, поэтому команда uname -n выполняется один раз, при первом сохранении. В некоторых интерпретаторах shell, например в bash и pdksh, строку PS1 можно заключать в одинарные кавычки. При этом в строке сохраняются обратные кавычки С), которые shell интерпретирует перед каждым выводом приглашения. (В данном случае это не имеет смысла, поскольку результат выполнения команды uname -n всегда один и тот же. Этот способ удобен, если в приглашении нужно постоянно обновлять информацию.) Дополнительные сведения вы найдете в параграфе 8.14. Поскольку одни и те же Escape-последовательности не работают на всех терминалах, целесообразно добавить в сценарий условную конструкцию if (47.03). Эта конструкция обеспечивает установку приглашения только в том случае, если терминал, тип которого записан в переменной TERM, принадлежит к серии VT100 фирмы Digital Equipment Corporation (или эмулирует этот режим). В табл. 7.1 приведен ряд Escape-последовательностей для терминала VT100 и совместимых с ним терминалов. Обозначение "ESC" в каждой последовательности соответствует символу ESCAPE. Таблица 7.1. Escape-поспедоватепьности для выделения текста иа терминале VT100 Последовательность ESC [lm ESC [4m ESC [5m E3C [7m ESC [Om Функция Полужирный шрифт Подчеркивание Мигание Инверсное изображение Выключение всех атрибутов юо - Часть первая. Работаем дома
7.09 Разумеется, вы должны использовать другие Escape-последовательности, если того требует ваш терминал. Чтобы определить правильные Escape-последовательности, следует воспользоваться такими программами, как tput и tcap (41.10), и прочитать записи, соответствующие вашему терминалу, в базе данных terminfo или termcap. Сохраните эти последовательности в переменных shell (6.08). Bash интерпретирует восьмеричные коды в строке приглашения, поэтому две команды из предыдущего примера можно упростить, превратив в строку, которая приведена ниже. При желании замените обратные кавычки ('...') символами $ ( и ) р.ну. PSl="\033[5mroot\033[0m4name -n~# " Версии tcsh, в которых не применяются восьмеричные коды, также позволяют вставлять в строку приглашения Escape-последовательности для создания эффекта мигания, установки полужирного шрифта, подчеркивания и т.д. Например, последовательность %s включает режим мигания, a %s — выключает. Так, для создания приглашения с мигающим словом root можно воспользоваться следующей строкой (вместо последовательности %т интерпретатор tcsh вставляет имя сервера): set prompt = '%Sroot%s@%m# ' - JP 7.09 Использование переменной SHLVL для отображения уровня порожденного интерпретатора shell При временном выходе в shell (30.26) или запуске порожденного интерпретатора (38М) вы можете забыть, что работаете в неинтерактивном интерпретаторе shell. Это чревато рядом неприятностей: может быть испорчен перечень ранее введенных команд (ii.oi), переменные shell (fi.os) будут не установлены и т.д. Интерпретаторы tcsh (S.03) и bash (ш> имеют встроенную переменную среды (e.oi) SHLVL, которая показывает, какой уровень занимает текущий интерпретатор в иерархии порожденных интерпретаторов. В этом параграфе показано, как создать такую переменную для С shell. (Подобным образом можно провести инициализацию н в ksh при установке переменной среды ENV ((.оз).) Интерпретатор tcsh (как и csh по завершении описанной ниже инициализации) также имеет переменную shlvl, содержащую ту же информацию. Если ваш shell является исходным интерпретатором, то переменная $shlvl равна 1. В первом порожденном интерпретаторе shell она равна 2, в следующем — 3 и т.д. Эту переменную можно использовать для управления выполнением файлов конфигурации, например, для выполнения ряда команд при входе в систему (когда переменная $shlvl равна 1), но не в порожденных интерпретаторах. Можно также поместить переменную $shlvl в строку приглашения (zoo (но только в порожденных интерпретаторах shell), что послужит напоминанием о том, что это процесс ' не высшего уровня. В shell высшего уровня приглашение может иметь вид mike%, в процессе первого уровня — (1) mike%, в процессе второго уровня — (2) mike% и т.д. Ниже приведен пример сценария для установки приглашения в файле .cshnr. # Если это порожденный интерпретатор shell, то необходимо вывести # в приглашении номер уровня If ($SHEVL == 1) 'then set prompt="$(USER}% " else set prompt="($SHLVL) ${USER}% " endif Bash не требует условной конструкции, поскольку интерактивные интерпретаторы shell читают файл .bashjrofile (или .profile), а порожденные — файл .bashrc. Вот команды для установки первичного приглашения, о котором речь шла выше: PSl=-='\u\$ ' ...для файла .bash_profile PS1=' ($SHLVL) \u\$ ' ., .для- файла .bashrc Установке строки приглашения 101
7.09 Bash и tcsh применяют одну и ту же переменную среды, поэтому можно запустить один интерпретатор shell из другого, и уровень процесса будет определен правильно. Ниже показано, как заставить csh работать с tcsh и bash или с таким же интерпретатором shell, если другие интерпретаторы не используются. Поместите в файл .cshrc* следующие строки: # Команды для shell высшего уровня и для удаленного интерпретатора shell (rsh) if (! $?SHLVL) then # Этот раздел читают как интерактивные, так и неинтерактивные # интерпретаторы shell. # Переменная $SHLVL пока не установлена, установите ее, чтобы показать, что ото # интерпретатор shell высшего уровня; ниже значение переменному величивается на 1: setenv SHLVL О endif # Установка уровня Интерпретатора shell (глубины вложения). # (Обратите внимание: csh не может выполнять арифметических операций # с переменными среды.) set shlvl = $SHLVL @..Л+47.04 @ Shlvl++ setenv SHLVL $shlvl $?prompt 2.09 if ($?prompt) then # Этот раздел читают только интерактивные интерпретаторы shell: ...введите здесь команды установки приглашения (см. выше) endif Вы работаете с многооконной системой, запускаемой из файла инициализации shell высшего уровня (например, .login)? Если да, то команды, приведенные в следующем примере (рассчитан на файл .login), переустанавливают переменную SHLVL так, что в окне терминала запустится интерпретатор shell со значением SHLVL, равным 1. В данном примере предполагается, что первый интерактивный shell запускается с порта /dev/console, а интерпретатор, запускаемый в окне, не взаимодействует с этим портом. (Если вы не уверены в этом, выполните команду who (SIM).) Вам, возможно, понадобится адаптировать пример. Фокус в том, чтобы перед запуском многооконной системы установить переменную SHL VL в нуль. Когда интерпретатор shell запускается в окне, он устанавливает переменную SHLVL в 1: # При работе на консоли рабочей станции измените значение SHLVL # и сразу запустите X Window: if (" Vbin/tty'" == /dev/console) then setenv SHLVL 0 xinit # Запуск X Window endif Адаптация примера к разным ситуациям (таким, как выполнение команд rsh ц.зз) и su (22.22), временный выход в интерактивный или неинтерактивный shell (30.26), запуск порожденного интерпретатора shell, работа в многооконной системе, выполнение заданий at (40.03) и т.д.) может оказаться трудной задачей (2.о?) и потребует планирования. Обдумайте все способы запуска порожденного интерпретатора shell. Установите, какие интерпретаторы являются интерактивными, а какие — нет, и получают ли они от своего родительского процесса переменную SHLVL, что можно проверить при помощи команды em или printenv (t.oi). Затем спланируйте, какие установки переменной SHLVL необходимы в различных интерпретаторах shell. Если учет всех перечисленных случаев окажется сложным для вас, проанализируйте в программе только наиболее важные варианты. Описанная система слишком удобна при использовании большого количества порожденных процессов, чтобы пренебречь ею. - JP Вы работаете с cxli и tcsli и при этом используете для обоих интерпретаторов shell один и тот же файл .lyluc! Интерпретатор tali не должен выполнять три строки, начинающиеся с set shlvl, он сам устанавливает эти переменные. Поместите эти строки между операторами if (| $tcsh) и endif. 102 Часть первая. Работаем цоШ
7.11 Г I 7.10 Чем может быть попезна пустая строка приглашения? [Если для вставки командных строк в окне вы применяете мышь, то по достоинству оцените описанный ниже прием.} Некоторые терминалы (например, старые терминалы Hewlett-Packard и Tektronix) имеют встроенные возможности редактирования. Они позволяют сместить курсор вверх по экрану к предыдущей командной строке, отредактировать ее и повторно послать серверу, нажав клавишу [SEND LINE]. Это не имеет никакого отношения к сложному процессу редактирования командной строки (п.щ, который сегЪдня обеспечивают многие интерпретаторы shell. Возможно, ваш терминал способен выполнять то же самое. При этом возникает следующая проблема. Если не удалить из командной строки символ приглашения shell (%), он будет послан обратно, что спровоцирует выдачу сообщения об ошибке: %: Command not found. Поэтому я установил следующее приглашение shell: set prompt=' ' Да, четыре пробела. Большинство команд UNIX начинают свой вывод с первой позиции на экране. Мои командные строки легко найти, так как они имеют отступ, a shell не обращает внимания на то, что командной строке предшествуют четыре пробела. Все работало прекрасно, пока я не получил новый терминал, в котором нет клавиши [SEND LINE]... (Если вы хотите получать в приглашении некоторую информацию, создайте, многострочное приглашение (zos) с четырьмя пробелами в последней строке.) - JP 7.11 Использование команды dirs вместо переменной cwd в строке приглашения С shell помещает путевое имя текущего каталога в переменную cwd (14.13), которая часто применяется в строке приглашения. При работе с командами pushd и popd (ЫМ) сложно запомнить содержимое стека каталогов (мне, во всяком случае, это не удается). Было бы удобно, чтобы на экране присутствовал перечень каталогов стека. Кроме того, не хотите ли вы сократить до тильды (~) имя начального каталога, чтобы оно занимало меньше места в приглашении? Вот как это можно сделать: запустите команду dirs и используйте ее вывод в строке приглашения. Простая команда cd, позволяющая решить все перечисленные задачи, выглядит следующим образом: i alias cd 'chdir \\* SS set prompt="'dirs'% '" Само приглашение имеет следующий вид: /work/project % cd ~ % cd bin -/bin % Вот что нужно поместить в файл .cshrc для создания многострочного приглашения (7.us), демонстрирующего содержимое стека каталогов: # Запомнить имя_сервера.домен.имя в $hostname, а имя_сервера в $HQST uname-п 50.07 set hostname 'uname -n " expr<5.2* setenv HOST 'expr $hostname : '\([Л.]*\).*'' alias setprompt 'set prompt="\\ H$(USER}@${HOST} "dirs'W \! % "■ alias cd 'chdir \\* SS setprompt' csti init alias pushd 'pushd \\* SS setprompt' ih_mit alias popd 'popd \\* SS setprompt' setprompt # УСТАНОВКА НАЧАЛЬНОГО ПРИГЛАШЕНИЯ Установка строки приглашения 103
7.12 Благодаря способности bash выполнять команду каждый раз при установке приглашения, а также благодаря наличию в этом интерпретаторе встроенных операторов установки приглашения (7М), версия приведенного выше сценария для bash помещается в одной строке: $(...) 9.16 PSl='\n\u@\h $.(dirs)\n\! \$ ' В результате выполнения сценария в начало каждого приглашения помещается пустая строка. Если вам это не нравится, объедините первую и вторую строки пользовательской команды setprompt или удалите первые символы \п. Пошлите в стек несколько каталогов и посмотрите, какое получится приглашение: jerry@ora ~ 1 % pushd /woxk/sxc/pexl /work/src/perl ~ jerry@ora /work/src/perl ~ 2 % cd ../cnews jerry@ora /work/src/cnews ~ 3 % pushd -/bin -/bin /work/src/cnews ~ jerry@ora -/bin /work/src/cnews ~ 4 % ^l£ Конечно, приглашение выглядит несколько громоздко, поскольку каждая команда pushd /*> выводит также результат выполнения команды dirs. Однако после выполнения еще нескольких команд присутствие в приглашении стека каталогов станет удобным. Если в стеке каталогов [14131 много записей, первая строка приглашения может превысить ширину экрана. В таком случае сохраните вывод команды dirs в массиве интерпретатора shell (47.os) и отредактируйте его при помощи редактора sed или встроенного в csh строчного редактора (9.щ. Чтобы на выходе команды dirs показывать только конец каждого путевого имени, воспользуйтесь приведенной ниже командой setprompt. Оператор С shell :gt удаляет в путевом имени все имена, кроме последнего: alias setprompt 'set dirs= ("dirs"); set prompt="\\ (g) ] ${USER}@3{HOST} $dirs:gt\\ \! % cshjnit _ ^ Следите за приглашением. Если вы забыли значения имен в приглашении, введите dirs: jerry@ora bin cnews jerry 5 % pushd -/tmp/test ~/tmp/test ~/bin/work/src/cnews ~ jerry@ora test bin cnews jerry 12 % dirs ~/tmp/test -/bin /work/src/cnews ~ В параграфе 47.05 приведен пример сохранения стека каталогов в массиве. - JP 7.12 Инициализация переменных сигналами от внешних команд В Bourne shell команда trap (44.12) запускает одну или несколько команд, когда интерпретатор получает сигнал (зш> (обычно от команды kill). При этом могут выполняться любые команд-', в том числе команды установки переменных shell. Например, интерпретатор может повторно прочесть файл конфигурации (см. параграф 38.11). Кроме того, может быть установлено новое значение переменной PSI, в которой содержится строка приглашения, что обеспечит ее обновление каждый раз, когда внешняя команда (например, другой сценарий или задание^ запущенное программой егоп (40.12)) посылает интерпретатору shell сигнал. Этот ряд примеров можно продолжить. 104 Часть первая. Работаем й<н# а
7.12 sh iiiit В следующем примере используется сигнал с номером 5, за которым обычно не закрепляются какие-либо действия. Когда shell получает сигнал 5, команда trap запускает команду определения даты и времени, а затем переустанавливает приглашение. Фоновое задание (1.27) активизирует этот процесс каждую минуту. Поэтому после ввода любой команды приглашение будет изменяться ежеминутно. При этом можно запускать любые команды: для подсчета количества пользователей, определения средней загруженности системы (39.от) и т.д. Новые интерпретаторы shell, например bash, способны выполнять команды, которые заключены п обратные кавычки (9.к), при каждом выводе приглашения (см. параграф 7.08). Для обновления переменной shell внешней командой в произвольные моменты времени лучше использовать команду trap. А теперь перейдем к рассмотрению примера введения даты и времени в приглашение старого интерпретатора Bourne shell. Если в вашей системе команда date не распознает форматы даты (например, +%а), замените ее той, которая делает это, например версией, предоставляемой на компакт-диске (si.io). Введите в свой файл .profile следующие строки (или наберите их по приглашению Bourne shell): # Введение даты и времени в приглашение; обновление — каждые 60 секунд: trap 'PSl='date "+%a %D %H:%M%n"~\ $\ '5 while : do sleep 60 kill -5 $$ done-s promptpid=$ !■ . ■ Теперь после ввода команды приглашение будет изменяться ежеминутно: Моп 02/17/92 08:59 $ сс bigprog.o undefined sybol first referenced in fils' xputc bigprog.o Id fatal: Symbol referencing.errors. Mon 02/17/92 08:59 $ Is bigprog. с bigprog.o Mon 02/17/92 09:00 $ Какой установить формат приглашения, зависит от вас. В этом примере создается двухстрочное приглашение (zos), в котором используются.символы обратной косой черты, позволяющие скрыть символы перевода строки и пробела от команды trap. Создать однострочное приглашение было бы проще. На странице руководства, поевящеиной команде date, указано,, что именно можно, вставить в приглашение. Этот пример начинается с цикла while (Ш0), выполняемого в фоновом режиме. В переменной promptpid хранится идентификатор интерпретатора shell, запущенного в фоновом режиме (замз). Перед выходом из. системы вы должны разорвать (за.щ цикл, что выполняется посредством следующей команды: kill $promptpid Эту команду вы можете ввести в командной строке или вставить в файл, который выполняется при выходе из системы (З.ю). - JP Установка строки приглашения 105
7.13 7.13 Выполнение команд перед выводом приглашения Bash может выполнять одну или несколько команд UNIX перед выводом приглашения. Такая команда не устанавливает приглашение. Просто ей предоставляется честь быть выполненной до вывода приглашения. Указанная команда может производить некоторые системные проверки, переустанавливать переменные shell или выполнять любые команды, вводимые по приглашению. Команды, которые активизируются до вывода приглашения, следует занести в переменную PROMPTCOMMAND. Учтите, что если они выполняются медленно, то задерживается вывод приглашения. Вот пример определения такой переменной: PROMPT_COMMAND=' # Сохранить старое значение $IFS; присвоить переменной IFS символ табуляции OIFS="$IFS"; IFS=" # Занести х в $1, face в $2, explain в $3 [, $4 ...]: set х 'smiley" (I Занести face в $face, а объяснение (я) в $explain: face="?2"; shift 2; explain="$*" # Восстановить переменные shell shift S#; IFS = "$OIFS'" # Приглашение, которое я использую (включает последнее значение $face): PSl='\u@\h $face • В первой части примера находятся команды shell, которые сохраняются в переменной PROMPT COMMAND. Они окружены парой одинарных кавычек (' '), одна — в конце первой строки (после знака =), а вторая — после команды изменения значения переменной IFS. Данная цепочка команд выполняется перед выводом каждого приглашения. Она присваивает новые значения двум переменным shell, $face и Sexplain, перед установкой каждого приглашения. Само приглашение устанавливается в последней строке. Оно включает значение переменной $face. Вот как выглядит мой экран после проведения этой "веселой" инициализации. Обратите внимание на то, что строка приглашения изменяется, когда изменяется переменная PROMPT_COMMAND (после изменения переменных $face и $explain). Чтобы получить описание появляющегося в процессе работы символа лица (face), я ввожу команду echo "$explain": jerry@ruby :-() echo "$explain" normal smiling face with a moustache # Обычное улыбающееся лицо с усами jerry@ruby +<I|-) vi proj.cc jerry@ruby :-0 echo "$explain" Mr. Bill # О, мистер Билл, у вас огромный рот, как у Мика Джаггера! Wow! ohh, big mouth, Mick Jagger uh oh jerry@ruby :-) < g++ -Wall proj.ee (Создавать такое приглашение еще более бесполезно, чем заниматься психоанализом (.u.w, но использовать его было довольно весело.) А теперь серьезно. Повторю еще раз: командная строка PROMPT_COMMAND не обязательно должна устанавливать приглашение. Ее можно применять для выполнения любой команды. Если команды, содержащиеся в переменной PROMPTCOMMAND, направляют результаты своей работы на стандартный вывод или в стандартный поток ошибок, вы увидите их перед приглашением. - JP 106 Часть первая. Работаем дома
Часть вторая Поручите грязную работу компьютеру Заставить компьютер выполнять рутинную работу — одна из главных идей книги. В следующих шести главах описаны основные способы достижения этой "благородной" цели. Интерпретаторы shell в UNIX позволяют несколькими способами предотвратить неоднократный ввод одних и тех же данных, а также длинных строк, которые могут быть сокращены. Пословица гласит: "Боги не учитывают время, проведенное на рыбалке". Я не знаю, как насчет рыбалки, но точно уверен, что время, потраченное на глубокое изучение возможностей интерпретатора shell, не пропадет даром. - TOR
8 Как shell интерпретирует команды 8.01 Назначение интерпретатора shell Shell отвечает за интерпретацию команд, которые вводит пользователь. Наибольшее распространение получили четыре-пять видов интерпретатора shell. Наряду с ними существует еще несколько видов (i.os). Задача интерпретации вводимых команд может показаться довольно простой, однако с момента нажатия клавиши [RETURN] до появления нужного результата происходит множество событий. Процесс интерпретации очень сложен: shell должен разбить команду на слова и подставить команды, заданные псевдонимами (Ю.ю), операторы из перечня ранее введенных команд (ii.oi), а также переменные shell и среды (6.os,6.oi). Он также инициализирует потоки стандартного ввода и вывода (i3.oi) и выполняет целый ряд других задач. Если, с вашей точки зрения, команда введена правильно, но работает неверно, причины могут быть следующими: • права доступа к файлам установлены неправильно; • вы не понимаете, как shell обрабатывает вашу командную строку. Ошибки, связанные с правами доступа, являются типичными и могут быть легко устранены. Сложнее понять правила, по которым shell обрабатывает командную строку. Я вас немного напугаю: материал, изложенный в настоящей главе, нужно проходить медленно. Грамотный пользователь должен понимать способ интерпретации команд. В этой главе мы рассмотрим, как shell интерпретирует команды. Стандартные интерпретаторы shell (С shell, Bourne shell и Korn shell) выполняют интерпретацию по схожим правилам. В некоторых случаях с С shell работать труднее. Со временем вы можете обнаружить в Сети (53.01) новый интерпретатор shell с совершенно иными правилами интерпретации. Хорошо это или плохо, но такова система UNIX. В процессе обсуждения мы коснемся механизма использования кавычек. С его помощью можно отменить специальное назначение, которое интерпретатор shell присваивает некоторым символам. Этот механизм используется при обработке командной строки. - ML 8.02 Введение в bash До начала 90-х большинство пользователей UNIX работали с С shell и Bourne shell в интерактивном режиме. Korn shell (расширенный вариант Bourne shell) появился в середине 80-х. Его популярность постоянно возрастала. В последнее время все большую известность завоевывают два бесплатно распространяемых интерпретатора с расширенными возможностями — tcsh (а.оз) и bash. В данном параграфе вы познакомитесь с последним из них. Как shell интерпретирует команды 109
8.03 "Bourne-again shell"* разработан организацией Free Software Foundation, которая выбрала название с присущим ей юмором. Несмотря на шутливое название, этот интерпретатор довольно серьезен. Вот уже 15 лет я использую различные интерпретаторы shell, но был поражен, прочитав об интерпретаторе bash в книге Learning the bash Shell издательства O'Reilly & Associates. С тех пор я применяю bash в качестве интерактивного интерпретатора. Он имеет множество возможностей, которыми ksh и csh (и особенно sh) не обладают. В то же время нельзя утверждать, что он перенасыщен ими. 1. В bash насчитывается более 60 переменных shell (б.щ. Они позволяют получать информацию для файлов конфигурации в.ю), а также задавать ту конфигурацию, которая необходима. 2. Я люблю насыщать информацией строку приглашения (zos). (Пока что я не вставил в приглашение значение температуры в Рио де Жанейро, но работаю над этим. :-)) Bash позволяет включать данные в приглашение посредством как внутренних, так и внешних команд UNIX. Кроме того, он обеспечивает оперативное обновление приглашения. Он позволяет также выполнять внутренние и внешние команды и отображать результаты их работы (если таковые имеются) до вывода приглашения. Например, интерпретатор bash может проводить быструю системную проверку и помещать в строку приглашения (или в верхнюю часть окна) замечания, предупреждающие о возникновении проблем, подсчитывать количество пользователей, сообщать, кто вошел в систему или вышел из нее, и т.д. 3. Интерпретатор bash поддерживает редактирование командной строки (п.и) в двух режимах — в стиле редакторов vi и Emacs. Существует также старый механизм подстановки команд из перечня ранее введенных (П.от). Во многих случаях быстрее набрать краткие выражения (Лк или даже !lpr :gs/2/3), чем просматривать и редактировать перечень предыдущих команд. Bash позволяет выбирать режим редактирования. 4. Распространяя свои программы среди других пользователей, я ориентируюсь на Bourne shell, поскольку пока немногие применяют bash. Но при решении своих задач, например, при написании сценариев и функций, я использую многие из его возможностей: мощные операторы обработки строк, операции проверки файлов и встроенной целочисленной арифметики. Bash постоянно обновляется. Версия на компакт-диске может оказаться устаревшей, когда попадет к вам. Если вы захотите получить интерпретатор с рядом новых возможностей, с несколькими устраненными ошибками (и, возможно, с несколькими новыми), воспользуйтесь Сетью и найдите его последнюю версию в GNU-архиве. - JP 8.03 Введение в tcsh В параграфе 8.2 мы представили вам bash и поговорили об интерпретаторах shell, которые появились до него. Многие пользователи отдают предпочтение tcsh. Этот интерпретатор похож на С shell, но по сравнению с ним имеет множество дополнительных возможностей. Кроме того, в нем устранены пресловутые ошибки С shell (47.02). В сущности, tcsh настолько похож на csh (за исключением упомянутых ошибок), что когда в книге мы говорим "С shell" или "csh", то имеем в виду и tcsh. Интерпретатор tcsh обладает многими возможностями bash. Поэтому мы не будем повторять список, приведенный в параграфе 8.2. Вместо этого рассмотрим некоторые различия. • Моя самая любимая особенность tcsh — команда rm *. с — показана в следующем примере: % га * .с Do you really wane to delete all files? [n/y] n С моей точки зрения, tcsh лучше контролирует содержимое командной строки, чем bash. * Игра слов: "Boume-again" ассоциируется с "born again", т.е. "заново рожденный". — Примеч. иерея. ПО Часть вторая. Поручите грязную работу компьютер]/
8.04 • Из-за неуклюжих пальцев в числе моих фаворитов — функция автоматической коррекции имен команд. В следующем примере я ввел команду srot. Интерпретатор tcsh не выдает бездумно сообщение о том, что команда не найдена (Command not found), а спрашивает, не имел ли я в виду команду sort: % who | srot +3n -1-4 CORRECT>who I sort +3n +4 (y|n|e|a)? у kim pts/0 Jul 27 14:40 (rock.ny.ora.com) jpeek pts/1 Jul 28 08:09 (jpeek.com) • Как и в csh, в tcsh есть массивы (47.0S). Я обнаружил, что они действительно полезны как в интерактивном режиме, так и в сценариях. (В bash их не было до версии 2.0.) • Отмечу один недостаток: способы установки переменных shell являются менее гибкими, в частности, сложнее изменять строку приглашения. В частности, изменение строки приглашения требует написания специальных пользовательских команд (правда, есть удобные встроенные операторы, например %с2, который выводит на экран два последних ^^^■j члена путевого имени текущего каталога). Г (9) I Если вы применяете интерпретатор csh и вам приходится каждый день набирать много команд, ^J^^| попробуйте tcsh. Вы найдете его на прилагаемом компакт-диске. tcsh jp 8.04 Интерпретация команд и случайная перезапись файлов Прежде чем углубиться в детали интерпретации команд, рассмотрим простой пример, демонстрирующий, насколько важно понимать этот процесс. Довольно часто встречается следующая ошибка. Предположим, у нас есть два файла: fllel и flle2. Необходимо создать новую версию файла fllel, к которому добавлен файл flle2. Это задача для команды cat, поэтому вводим: % cat filel file2 > filel ...неправильно Как будто должно сработать. Но если попробовать, то окажется, что нет. Эта команда удаляет содержимое файла fllel, а затем помещает в него содержимое файла file2. Почему? Потому что стандартным вводом-выводом управляет shell, а не команда cat. • При обработке команды shell видит, что стандартный вывод перенаправлен в файл fllel, поэтому интерпретатор открывает файл для записи, разрушая содержащиеся в нем данные. • По завершении интерпретации командной строки shell выполняет команду cat, передавая ей имена fllel и flle2 в качестве аргументов. Но файл filel уже пуст. • Команда cat читает файл fllel (пустой) и записывает его в стандартный вывод (который направляется в файл fllel). • Команда cat читает файл flle2 (который также направляется в файл fllel). На этом выполнение команды cat завершается, и управление передается интерпретатору shell. Файлы fllel и flle2 одинаковы, а это не совсем то, чего мы ожидали. Но так уж получилось. В подобной ситуации некоторые версии команды cat выводят предупреждение (cat: input filel is output), что может навести на мысль о разумности команды cat, которая смогла подстраховать вас. К сожалению, это не так. К тому моменту, когда команда cat выяснит, что входной и выходной файлы одинаковы, файл fllel будет уничтожен. Впрочем, указанная способность команды cat небесполезна: она предотвращает создание бесконечно длинных файлов с помощью команд следующего вида: % cat filel file2 » file2 - ML Как shell интерпретирует команды HI
8.05 8.05 Интерпретация командной строки Необходимо знать приоритеты различных механизмов подстановки в С shell. Вот в каком порядке С shell интерпретирует командную строку: • 1) подстановка ранее введенных команд из перечня; 2) выделение ключевых слов (в том числе специальных символов); 3) обновление перечня; 4) интерпретация одинарных (') и двойных (") кавычек; 5) подстановка содержимого команды, заданной псевдонимом; 6) переадресация ввода-вывода (например, >, < и I); 7) подстановка значений переменных; 8) подстановка результатов выполнения команд; 9) подстановка имен файлов. (В Bourne shell все происходит в том же порядке, за исключением того, что не выполняется подстановка команд из перечня и подстановка команд, заданных псевдонимом.) Подстановка команд из перечня всегда выполняется первой. Вот почему кавычки не скроют символ ! от shell: он распознает знак восклицания и выполняет подстановку до того, как заметит кавычки. Чтобы предотвратить подстановку команд из перечня, нужно использовать обратную косую черту (g.is). Поработаем с простой командной строкой, которая содержит несколько элементов из числа перечисленных. Вы поймете, что значит "shell выполняет подстановку значений переменных после подстановки псевдонимов команд". Вот эта командная строка (в ней встречаются как символы пробела, так и табуляторы): % Is -1 $НОМЕ/* | grep "Map 7" Строка интерпретируется следующим образом: 1. Подстановка команд из перечня (и.щ не выполняется из-за отсутствия соответствующих операторов. (Bourne shell пропускает этот шаг.) 2. Командная строка разбивается на слова по пробельным символам: Is, -1, $НОМЕ/*, |, grep, "Mar 7". Shell не обращает внимания на количество пробельных символов (пробелов и табуляторов) между словами в командной строке. Любой пробельный символ, который не заключен в кавычки, служит признаком нового слова. Shell не выполняет специальной обработки опций (в нашем случае 1). Опции передаются выполняющейся команде, как и любой другой аргумент,* после чего сама команда решает, как ее интерпретировать. Обратите внимание, что кавычки (З.ы) предупреждают разбиение фразы "Маг 7" на два слова, а два пробела между ними не будут проигнорированы," несмотря на то, что интерпретация кавычек производится позже. На данном этапе командная строка имеет следующий вид: Is -1 $НОМЕ/* I grep "Mar 7" 3. Командная строка заносится в перечень. Bourne shell не выполняет этого. 4. Shell распознает двойные кавычки в выражении "Маг 7" и отмечает, что интерпретировать специальные символы внутри кавычек не нужно. 5. Выполняется проверка того, не является ли команда Is или grep псевдонимом оо.ю). В данном случае это не так. 6. Shell отмечает, что для символа "|" выполнены все условия (U.oi), необходимые для создания канала. * Обозначение опции дефисом (-) является общепринятым соглашением. Хотя Ьбработка опций стандартизирована (44.1S), каждая команда может интерпретировать свои опции произвольно. ** В списке, который выводится командой Is -I, число в дате, которое меньше 10, выводится с двумя пробелами впереди. 112 Часть вторая. Поручите грязную работу компьютеру
8.06 7. Переменная среды (б.ог) НОМЕ заменяется значением /home/mikel. На данном этапе командная строка имеет следующий вид: Is -1 /home/mikel/* I grep "Mar 7" 8. Shell ищет обратные кавычки (в.к), выполняет заключенные в них команды и подставляет результаты в командную строку. В данном случае эти символы отсутствуют. (Если в кавычки заключены также специальные символы или имена переменных, они не интерпретируются, пока shell не выполнит все команды в кавычках.) 9. Производится поиск специальных символов ц.щ. В данном случае shell обнаруживает символ * и заменяет его именами файлов, вследствие чего формируется следующая командная строка: Is -1 /home/mikel/ax ... /home/mikel/zip I grep "Mar 7" 10. Выполняются команды Is и grep, причем выходные данные команды Is передаются по каналу на вход команды grep. В командных строках часто встречается точка с запятой. Этот символ используется в качестве разделителя между командами: сначала набирается одна полная командная строка, затем вводится точка с запятой (клавишу [RETURN] нажимать не нужно), а после нее — еще одна полная командная строка. Объединение команд с помощью таких символов, как точка с запятой, особенно удобно при запуске порожденного интерпретатора shell (1з.от), а также при создании псевдонимов команд и в списках команд озщ (книга содержит множество примеров, подтверждающих это; ознакомьтесь хотя бы с примерами в параграфах 40.02 и 10.02). Дополнительные материалы по механизму интерпретации командной строки вы найдете в параграфах, посвященных специальным символам внутри псевдонимов команд (ЯЩ, команде eval (s.ro), условным конструкциям (44.09) и во многих других. [Некоторые тонкие и даже забавные моменты работы С shell освещены в параграфе 8.12, написанном Крисом Тореком, который я рекомендую вам прочесть. — JP\ - DG, ML 8.06 Ввод аргументов командной строки Команда echo направляет значения своих аргументов и символ новой строки в стандартный вывод. Сценарии shell используют команду echo дня вывода текста на терминал, в канал или в файл. Пользователь может применять ее в командной строке для вывода значения переменной (6.oi, e.os), чтобы увидеть, как подставляются специальные символы в именах файлов (причем над самими файлами никакие действия не производятся), или проверить действие кавычек (И.щ. % echo "USER is $USER. " USER is jerry. % echo "All 'a' files are: " a* All 'a' files are: abacus apple axes Команда print/ обладает более широким кругом возможностей форматирования. Переносимость С shell и большинство новых интерпретаторов shell содержат встроенную версию команды echo, которая работает быстрее. Изначально команда echo и ее эквивалент в csh, команда which, имели только одну опцию. Опция -п указывала команде, что в конце сообщения не нужно выводить символ новой строки. В сценариях shell опция -и применяется для организации диалога, когда курсор остается в конце сообщения: echo -n "Enter your name: " (Пробел в конце улучшает вид приглашения. Кавычки обеспечивают передачу пробелов команде echo.) Как shell интерпретирует команды 113
8.06 Команда echo новых версий проверяет свои аргументы на наличие обратной косой черты (\). Этот символ является обозначением начала Escape-последовательности, в которую наряду с ним входит еще один, следующий, символ. Например, когда команда echo одной из последних версий находит символы \п, она выводит символ новой строки: $ echo "I.\n2.\n3." 1. 2. 3. $ В этих версиях команды echo символы \с в конце последнего аргумента подавляют вывод символа новой строки (в старых версиях для этого предназначены символы -п): echo "Enter your name: \c" На странице руководства, посвященной команде echo (или csh), указано, какая версия установлена у вас, а также приведен список всех Escape-последовательностей. С новыми версиями команды echo связана одна проблема: они искажают строки, в которых случайно оказалась обратная косая черта. Крис Торек нашел выход: вместо команды echo надлежит использовать конструкцию "документ здесь" («) (я.щ и команду cat (25.02). Например: cat « END The answer is: $переменная_с_обратной_косой_чертой END Пользователям bash повезло: в этом интерпретаторе команда echo имеет опцию -е, которая активизирует интерпретацию символа обратной косой черты, и опцию -Е, прекращающую ее. Еще одна утилита — printf— работает как подпрограмма printfft) в языке С: она обрабатывает ; Escape-последовательности, позволяет устанавливать ширину полей и т.д. (GNU-версия находится на компакт-диске.) Приведем пример. Команда wc выдает количество строк, слов printi и символов в файле, а вслед за этим — имя файла. Значения этих четырех полей передадим . с выхода команды wc утилите printf с помощью обратных кавьгчек (9.щ. (Формат вывода : команды wc описан в параграфе 29.06.) Утилита printf получает инструкции по форматиро- ; ванию из своих аргументов. Первая инструкция (%4$s) — вывести содержимое четвертого поля (имени файла). Затем с помощью пяти символов с ведущими нулями (%2$05s) выводится •: содержимое второго поля (количество слов). В завершение выполняется вывод содержимого первого поля (%l$s — количество строк) с символом новой строки (\п) в конце строки. $ printf 'The %4$s file has %2$05s words on %l$s lines.\n' 'wc count' The count file has 00235 words on 42 lines. Насколько мне известно, команда printf не встроена ни в один интерпретатор shell. Поэтому : она обладает лучшей переносимостью, чем разношерстый набор команд echo. Если бы у нас , были только команда printf и старая команда echo, жизнь была бы прекрасной. В параграфе - 46.10 показано, как обеспечить переносимость команды echo. В С shell команда echo работает не так, как в других интерпретаторах. Например, для вывода пустой строки стандартной команде echo не нужно передавать никаких аргументов. (Пустые ~ строки между блоками вывода повышают удобочитаемость.) Стандартная команда echo просто . выводит символ новой строки: $ echo $ В С shell команда echo не выводит символа новой строки. Для его получения нужен пустой аргумент: ~ % echo "•• % ч Чтобы выполнить в С shell стандартную команду echo, введите /bin/echo. 114 Часть вторая. Поручите грязную работу компьютер!^
8.07 Создание сообщений об ошибках Команда echo направляет сообщения на стандартный вывод. Сообщения об ошибках в сценариях должны направляться в стандартный поток ошибок так, чтобы они не попали в переадресованный оз.оп стандартный вывод. В Bourne shell оператор 1>&2 (4S.21) перенаправляет вывод команды echo в стандартный поток ошибок: echo "progname: choke wheeze complain" 1>&2 В С shell это невозможно, что является еще одним доказательством нецелесообразности написания сценариев для csh (47.02). - JP 8.07 Установка последовательности поиска команд Последовательность поиска команд (6.04, ш) определяет, в каких каталогах и в каком порядке интерпретатор shell будет искать внешние (i.io) команды. Последовательность поиска, которая будет действительна при каждом входе в систему, устанавливается в файле конфигурации (2.02). Кроме того, ее можно изменить на какое-то время. Установка последовательности поиска в файлах конфигурации Чтобы изменить последовательность поиска, которая используется при каждом входе в систему, отредактируйте строку РАТН=.. . в файле .profile либо строку set path =■=(...) в файле .cshrc или .login. Используйте полные путевые имена (Ы.<в) каталогов. У вас есть следующие варианты: • Вы можете указать новый каталог в конце последовательности поиска. Тогда команды, содержащиеся в добавленных каталогах, будут выполняться только в том случае, если они имеют уникальные имена, которые не встречаются в предыдущих каталогах. Проверить уникальность имен позволяет команда which (so.os). • Если в последовательности поиска имя нового каталога поместить перед такими стандартными системными каталогами, как /bin, команды из данного каталога будут выполняться вместо системных команд с теми же именами. Это позволяет заменить команды, работающие неудовлетворительно, другими версиями. Например, если в вашем каталоге bin (4.02) есть сценарий со/для обработки текущей даты (4S.ot), он будет использован вместо системной команды cal (4S.o6). Если вы сформируете последовательность поиска таким образом, следите за тем, чтобы случайно не присвоить своей программе имя системной программы (в параграфе 44.21 объясняется, как проверить это). Не забудьте также сделать данный каталог недоступным для записи (при помощи команды chmod go-w), чтобы другие пользователи не смогли добавить в него плохие программы с именами системных утилит. Если в начало последовательности поиска каталога с пользовательскими командами вставить такие же имена, как и у системных команд (например, Is или mi), возможны тяжелые последствия. Многие системные программы и сценарии при вызове такой программы, как Is, ожидают, что она будет работать как исходная системная версия этой программы. Если она поведет себя по-другому, могут возникнуть серьезные проблемы. Например, в начале последовательности поиска вы установили версию команды rm, которая записывает в стандартный вывод сообщение Do you want to remove this file? (Хотите удалить этот файл?) и читает ваш ответ из стандартного ввода. Системная команда rm не выводит сообщения, если стандартным устройством ввода не является терминал. Если ваша команда rm не работает таким же образом, то другие программы могут зависнуть, бесконечно ожидая, пока не будет получен ответ. Желательно, чтобы имя новой версии команды отличалось от имени системной команды. Как shell интерпретирует команды 115
8.08 Когда осуществляется вход в систему и активизируется shell, система, скорее всего, установит исходную последовательность поиска команд до прочтения файлов инициализации. Системный администратор может изменить эту последовательность. Если в вашей системе имеется какая-то исходная последовательность поиска, ее можно использовать в качестве, основы вашей последовательности, посоветовавшись с системным администратором. Для создания последовательности поиска таким способом предназначена переменная $FATH или $path. Например, пользовательский каталог bin добавляется в конец последовательности поиска посредством одной из следующих строк:* set patn=(3paLh -/bin) С shell PATH=$PATH:$HOME/bin Bourne shell В Bourne shell загрузить обновленную переменную PATH позволяет такая команда: $ . .profile При использовании С shell введите одну из следующих команд, в зависимости от того, какой файл подлежит изменению: % source .cshxc % source .login Изменение последовательности поиска в командной строке В процессе работы может потребоваться изменить на время последовательность поиска. Например, когда я разрабатываю новые версии существующих команд, то помещаю их в отдельные каталоги с именами типа alpha-test. Обычно я не запускаю программы из таких каталогов, но если это все-таки необходимо, добавляю на некоторое время каталог alpha-test в начало последовательности поиска. (Удобно установить новую последовательность поиска при запуске порожденного интерпретатора shell (З8.04), тогда в других интерпретаторах последовательность поиска не меняется.) Это позволяет выполнить командная строка, которую можно было бы вставить в файл конфигурации: % set path=(-/xxx/alpha-test $path) С shell $ PATH=$H0ME/jooc/alpha-te8t:$PATH Bourns shell $ export PATH В параграфе 8.08 описан другой способ изменения последовательности поиска: путем перечисления команд, а не каталогов. - JP 8.08 Создание каталога невыполняемых команд Как предотвратить выполнение некоторых команд, находящихся в одном из каталогов, указанном в последовательности поиска (6.04, 6.05)? Например, я работаю на нескольких компьютерах. Я читаю и храню электронную почту о.зз) только на одном компьютере; на котором должны быть доступны все возможности электронной почты. На других компьютерах я хочу пользоваться только командами отправки почты. На этих компьютерах в моей учетной записи не должна быть предусмотрена возможность чтения сообщений электронной почты. Возможно, вы работаете над проектом в общедоступных файловых системах и хотите, чтобы ряд команд выполнялся только на определенных компьютерах. Как предотвратить-случайное выполнение команд там, где это не должно происходить? Ведь выполнение опасных команд должно быть запрещено начинающим пользователям. Как реализовать это? Для таких команд можно создать псевдонимы оо.о2), с помощью которых на экран выводятся сообщения. Однако поддерживать десятки или даже сотни псевдонимов нелегко. Установка последовательности поиска в файле .cshrc или ENV(ksh) вызывает небольшую проблему. Каждый запуск порожденного интерпретатора shell (зам) будет сопровождаться добавлением вашего каталога bin к последовательности поиска. Это не приведет к возникновению ошибок, но значительно удлинит строку последовательности. Во избежание этого используйте в качестве флага переменную среды, например ENV_SET<г.07), и устанавливайте последовательность поиска, когда эта переменная ие задана. 116 Часть вторая. Поручите грязную работу компьютеру
8.09 Вот как я решил эту проблему. На всех моих компьютерах команды почтовой системы, которая называется МН, содержатся в каталоге /usr/local/mh. Я создал каталог по_гип.имя_сер- вера, в котором находятся краткие сценарии. Эти сценарии имеют такие же имена, как и команды в каталоге /usr/local/mh, которые не должны выполняться. На компьютерах, где определенные команды не должны выполняться, в строке последовательности поиска я указываю каталог по_гип.имя_сервера перед каталогом /usr/local/mh: switch47.ee switch ("uname -n ') case cruncher: set path=( ... ~/no_run.cruncher /usr/local/mh ...) (Для этих целей можно также на каждом сервере (2.U) использовать свои файлы конфигурации.) Когда я ввожу команду, которая не должна выполняться, интерпретатор shell, прежде чем найти настоящую команду в каталоге mh, должен выполнить сценарий из каталога по_гип. Сценарий включает звуковой сигнал, выводит сообщение со своим именем и именем рекомендуемого компьютера и завершается: % inc сигнал... You can't run inc here. Use sunspot. С целью экономии дискового пространства все сценарии в каталоге по_гип представлены в виде жестких ссылок (им) на один и тот же файл: % Is -li rio_run.cruncher 270156 -rwxr-xr-x 31 jerry 82 Jun 12 09:10 inc 270156 -rwxr-xr-x 31 jerry 82 Jun 12 09:10 mark 270156 -rwxr-xr-x 31 jerry 82 Jun 12 09:10 msgchk ...всего 31 ссыпка... В сценарии используется команда basename $0 (4f.it), включающая имя команды в преду- (•) ] преждающее сообщение: #! /bin/sh echo "\007.You can't run 'basename $0' here. Use sunspot." 1>&2 I 44.07 exit 1 Код \007 в моей версии команды echo служит для включения звукового сигнала. В других версиях может понадобиться код \а или комбинация [CTRL-gl (4S.JS). В параграфе 16.15 приведен родственный сценарий. - JP no run 8.09 Специальные символы в псевдонимах команд Приведем еще один пример; в котором имеет значение последовательность интерпретации командной строки. Рассмотрим пользовательскую команду, подсчитывающую количество слов во всех файлах: we 29.06 % alias words "wc -w *" Данная команда наглядно демонстрирует, каково значение последовательности интерпретации командной строки. Shell обнаруживает кавычки, которые указывают, что заключенные в них специальные символы раскрывать не нужно. Следовательно, команда words будет содержать команду wc -w *, поскольку при создании команды значение символа * не подставлялось. (Если бы специальные символы стояли вне кавычек, подстановка была бы выполнена.) Теперь посмотрим, что произойдет, если выполнить команду. Вводим: % words Shell начинает обрабатывать командную строку (S.os) и в конце концов выполняет подстановку. Когда это происходит, командная строка приобретает следующий вид: fa* shell интерпретирует команды 117
8.10 А теперь — внимание! Shell продолжает интерпретацию: выполняет переадресацию ввода-вывода, подставляет значения переменных и результаты выполнения команд. Наконец, он доходит до подстановки имени файла. На этом этапе интерпретатор замечает в командной строке звездочку и заменяет ее именами файлов из текущего каталога. Выглядит довольно просто. Но задумайтесь: вы ведь не набирали символ *, shell сам установил его в этом месте, когда раскрывал знак подстановки. А что бы произошло, если бы shell обрабатывал специальные символы до раскрытия псевдонима команды? Тогда звездочка никогда не была бы заменена. К тому моменту, когда shell поместит ее в командную строку, интерпретация специальных символов подошла бы к концу и начался бы подсчет слов в файле с именем * (которого может и не существовать). Я не перестаю удивляться, что все это работает. И хорошо работает! Выполнение командной строки — сложный процесс, но интерпретатор shell, не задумываясь, делает то, что вам нужно. - ML 8.10 Повторный анализ командной строки: команда eval Если вы прочитали предыдущий параграф (в.ю), то убедились, что в большинстве случаев интерпретатор shell выполняет командную строку в определенной последовательности. Иногда эта последовательность нарушается. Рассмотрим ситуацию, с которой shell не справляется. Признаюсь, она несколько надуманна, но не слишком отличается от тех, которые складываютсв' в сценариях shell o.os): % set b=\$a % set a=foo % echo $b $a Предположим, необходимо вместо переменной $Ь подставить переменную $а и использовать ее значение. Но этого не происходит. Подстановка переменных выполняется только однажды и не является рекурсивной процедурой. Значение переменной $Ь равно "$а " и не более того. Однако существует выход. Команду eval можно трактовать следующим образом: "Дай мне еще один шанс. Повторно проанализируй строку и выполни ее". Вот что произойдет, если команду eval вставить перед командой echo: % aval echo $b too Shell преобразует переменную $b в $а, после чего команда eval повторно запускает процедуру анализа командной строки, преобразуя echo $а в echo £оо, чего мы и добивались с самого начала! Вот более реальный пример. Подобный код довольно часто можно встретить в сценариях для Bourne shell: command-'grep $grepopts $searchstring $file' for opt do case "$opt" in file) output^' > $ofile' ;; read) output=' | more' ; ; sort) postproc=' | sort $sortopts';; esac done eval $command $postproc $output 118 Чвсть вторвя. Поручите грязную рвботу компьютеру
8.11 Понимаете ли вы, что происходит? Мы конструируем командную строку, которая будет выглядеть примерно так: grep $grepopts $searchstring $file | sort $sortopts > $ofile Вся строка "спрятана" в переменных shell, в том числе оператор переадресации ввода-вывода и различные опции. При отсутствии команды eval картина была бы следующей. Мы получили бы сообщение I not found. Это связано с тем, что подстановка переменных происходит после переадресации вывода. "Вложенные" переменные (например, переменная $ofile, используемая в переменной $output) вообще не раскроются, что привело бы к выдаче такого сообщения: $ofile I not found. Команда eval, предшествующая другой команде, заставляет интерпретатор shell обрабатывать командную строку еще раз, что обеспечивает правильную подстановку значений переменных и переадресацию ввода-вывода. Команду eval трудно переоценить, особенно когда в переменные shell включены другие переменные, псевдонимы команд, операторы переадресации ввода-вывода и т.д. Обычно эта команда применяется в сценариях для обнаружения команд, которые создаются в процессе выполнения. Примеры использования команды eval приведены в параграфах 5,04, 10.07, 10.10, 45.17, 45.34, 46.03 и некоторых других. - ML 8.11 Какую команду выполнит bash? Параграф 8.05 содержит общий обзор действий С shell при интерпретации командной строки. Bash выполняет примерно те же операции. В данном параграфе мы рассмотрим, как контролировать выбор того, что должно быть выполнено — функция, встроенная или внешняя команда? (В параграфе 8.12 подробно и с юмором описано, как это происходит в С shell.) Предположим, необходимо написать функции shell с именами cd, pushd и popd. Эти функции должны запускать встроенные в shell команды cd, pushd и popd и вызывать другую функцию shell, setvars, которая выполнит некоторые установки в новом каталоге: cd() { pushd() { popd() ( в- 44.15 cd "$@" pushd "$@" popd "$@" setvars setvars setvars 1 1 1 Какую из команд cd выберет bash, если ввести cd: встроенную или вашу? (Тот же вопрос можно задать и по поводу popd и pushd.) Хуже того: команда cd "$@" внутри функции может заставить bash снова вызвать вашу функцию cd, вследствие чего образуется бесконечный цикл. Вы должны знать, как предотвратить это. Поставив перед именем команды ключевое слово command, вы предотвратите поиск функции shell. В этом случае bash выполняет только встроенные и внешние команды. Поэтому команды не будут выполняться повторно, если определить их следующим образом: cd() { command cd setvars 1 "$@" pushd () { command pushd "$@" setvars 1 popd О ( command popd setvars 1 "$@" Если в силу каких-то причин вы не хотите, чтобы выполнялась ваша новая функция pushd, можно однократно вызвать встроенную команду pushd следующим образом: bash$ command pushd каталог Ключевое слово command позволяет выполнять внешние команды (из каталогов, указанных в переменной PATH (б.м)) с заданными вами именами. Чтобы заставить bash выполнять встроенную команду, а не функцию shell или внешнюю команду, наберите перед именем команды ключевое слово builtin. Хотя bash всегда выбирает встроенную команду, прежде чем выбрать внешнюю, можно явно задать выполнение встроенной команды echo: builtin echo -n 'What next? ' far shell интерпретирует команды 119
8.12 А если нужно выполнить внешнюю команду echo? Наверное, проще всего указать полное путевое имя. Например, когда я редактировал параграф 8.20, то решил испытать четыре (!) версии внешней команды echo в System V. Поэтому я вводил примерно такие команды: bash$ /bin/echo hi \\ there Наконец, можно выключить определенные встроенные команды bash при помощи команды enable. В отличие от команд command и builtin, команда enable продолжает действовать до самого выхода из интерпретатора shell. Команда enable -n выключает одну или нескрлько встроенных команд, имена которых перечисляются как аргументы. Например, в своих экспериментах, связанных с параграфом 8.20, я мог обеспечить выполнение именно внешней команды echo, активизировав один раз первую из приведенных команд: bash$ enable -n echo bash$ type echo echo is hashed (/bin/echo) В интерпретаторе bash команда type подтверждает, что сейчас я использую внешнюю команду echo. Включить встроенные команды можно, набрав enable имя_команды. Команда enable -a позволяет определить статус всех встроенных команд bash. - JP 8.12 Какую кощнцу выполнит С shell? [В параграфе 8.11 рассказано о том, как в bash управлять выполнением встроенной команды, функции shell или внешней команды. Способ достижения того же в С shell немного другой. Крис Торек объясняет, почему. Например, команда \гтп делает недоступной пользовательскую команду хт, а команда \cd — встроенную команду cd. Крис дает довольно сложное объяснение и некоторые практические советы. Завершают параграф краткие и забавные выводы, -г- JP] С shell вначале разбивает каждую строку ввода на вектор слов, а затем сравнивает его с псевдонимами команд. Поскольку \rm не совпадает с пользовательской командой ил, псевдонимы игнорируются. После этого G shell обрабатывает кавычки (так как пользовательская команда может содержать множество слов, все это занимает некоторое время). С shell отмечает кавычки путем установки 8-го бита каждого байта для символов, заключенных в кавычки. Поскольку ' *' 10x80 [логическое ИЛИ кода символа с шестнадцатеричным значением 80 или двоичным значением 10000000 — JP] не совпадает с символом '*', с помощью этого приема предотвращается интерпретация специальных символов, дальнейшая разбивка на слова и т.д. В конечном итоге shell получает обработанную строку. Далее он сравнивает элемент word [0] [первое слово в командной строке — JP] с именами всех встроенных команд. Если есть совпадение, то выполняется соответствующая встроенная команда, и уже от нее зависит дальнейшая обработка остальных слов. Например, выполнение команды Is * в каталоге, содержащем только файл -/, приведет к выводу длинного списка, а команда j obs * выведет инструкцию по использованию. Если совпадений нет, shell заменяет специальные символы в именах файлов из текущего списка слов, создавая новый список. Затем интерпретатор делает следующее: a) удаляет 8-й бит каждого байта во всех словах; b) применяет команду exec к результирующей командной строке. Это значит, что команда \cd не приводит к выполнению команды, заданной псевдонимом cd. При проверке на совпадение со встроенной командой cd она имеет вид 'с'10x80, 'd', '\0' Часть вторая. Поручите грязную работу компьютеру'
8.13 и не совпадает со встроенной командой 'с', 'd', '\0' Вот почему встроенная команда cd не выполняется. Далее строка урезается, и shell ищет внешнюю команду cd. Если вы хотите предотвратить подстановку псевдонима, но сохранить выполнение встроенной команды, замените строку \cd foo или \rm foo строкой 11cd foo или ""rm foo Эти строки не совпадают с псевдонимами команд, поскольку на этапе анализа команды содержат по паре кавычек впереди. Однако они совпадают с именами встроенных команд, так как по прохождении данного этапа строки будут урезаны (7-й бит устанавливается для каждого символа между двумя кавычками, а в данном случае нет ни одного такого символа). Иногда благодаря тому, что обработка псевдонимов происходит до интерпретации специальных символов, можно сделать замечательные вещи путем выполнения вот таких команд: % [ Missing I. % alias [ echo foo % [ foo f (Псевдоним раскрывается до интерпретации специальных символов.) % unalias [ unalias: Missing ]. (Команда unalias заменяет специальные символы в своих аргументах.) % unalias \[ % alias unalias echo foo unalias: Too dangerous to alias that, t Создавать такой псевдоним # очень опасно (С shell пытается предупредить об опасности...) % alias \unalias echo foo % alias unalias (echo foo) % unalias unalias foo unalias (He удается!) % ''unalias unalias % alias % (Наконец-то выход.) — CT, из Usenet, 14 ноября 1990 г. 8.13 Особенности перенаправления ввода-вывода Почему при работе с Bourne shell и Кот shell только вторая из приведенных ниже команд перенаправляет стандартный выходной поток и поток ошибок (stdout и stderr) (u.oi) в файл? $ cat food 2>Sl >file cat: can't open food $ cat food >£ile 2>sl $ to' shell интерпретирует команды 121
8.14 Хотя в руководствах по многим версиям sh это не упоминается, аргументы интерпретируются слева направо. 1. В первой командной строке shell сначала обрабатывает символы 2>&1. Это означает: "Направить сообщения об ошибках (файл с дескриптором 2) на стандартный вывод (файл с дескриптором 1)". Это ни к чему не приводит, поскольку вывод обоих файлов уже направлен в терминал. Затем команда >file перенаправляет стандартный вывод (файл с дескриптором 1) в файл. Но стандартный поток ошибок stderr (файл с дескриптором 2) по-прежнему направляется на терминал. 2. Во второй командной строке shell сначала обрабатывает символы >file, перенаправляя стандартный вывод в файл. Далее оператор 2>&1 направляет стандартный поток ошибок туда же, куда направлен стандартный вывод, т.е. в файл. А это как раз то, что нам нужно. В параграфе 45.21 также говорится об операторе переадресации m>&n. - JP 8.14 Кавычки и обратная косая черта в Bourne shell Мне непонятно, почему многие считают правила использования кавычек и обратной косой черты в Bourne shell чрезвычайно сложными. Наоборот, они очень просты. (Правила использования этих символов в интерпретаторе С shell сложнее; см. параграф 8.15.) Основная идея состоит в следующем: кавычки и обратная косая черта предназначены для отмены особого назначения специальных символов. Обратите внимание, что обратная кавычка (') не входит в эту группу: она служит для подстановки результатов выполнения команды (ч.щ. Специальные символы Ниже перечислены специальные символы Bourne shell. Вероятно, вы уже использовали некоторые из них. С помощью кавычек можно отменить особое назначение специальных символов. (Да, кавычки и обратная косая черта входят в эту группу. Их действие также можно отменить. Но об этом — позже.) # S * ? [ ] ( ) = | Л ;<>■?"' \ Символы пробела, табуляции и новой строки тоже имеют особое назначение: они являются разделителями аргументов. Обратная косая черта (\) имеет особое назначение в системе UNIX, но не в shell, поэтому кавычки не позволяют изменить назначение этого символа. Правила использования кавычек и обратной косой черты Соответствующие правила приведены в табл. 8.1. Возможно, вы вернетесь к этой таблице при изучении примеров. Таблица 8.1. Отмена назначения специальных символов в Bourne shell Пример Действие 'ххх' Отмена специального назначения у всех символов в строке ххх "ххх" Отмена специального назначения у всех символов в строке ххх, за исключением символов $, * и \ \х Отмена специального назначения символа х. В конце строки символ \ отменяет действие символа новой строки; таким образом, следующая строка будет продолжением текущей Чтобы понять, действие каких специальных символов будет отменено, представьте себе следующее: Bourne shell читает командную строку или строки сценария символ за символом от первого до последнего. (На самом деле все гораздо сложнее, но в данном случае это не имеет значения.) Когда интерпретатор обнаруживает кавычки или обратную косую черту, он делает следующее: • удаляет этот символ; 122 Чвсть вторая. Поручите грязную работу компьютера
8.14 • отменяет специальное назначение некоторых или всех остальных символов согласно правилам, приведенным в табл. 8.1. Необходимо также знать, у скольких символов будет отменено специальное назначение. Далее приведены примеры применения правил. Если хотите, попробуйте ввести эти примеры по приглашению Bourne shell. (He пользуйтесь С shell, который работает по-другому (я./.у.) Чтобы запустить Bourne shell, введите команду sh. Для выхода из него наберите [CTRL-d]. • Обратная косая черта (\) отменяет специальное назначение следующего символа (если он есть). Например, \* соответствует символу *, а не специальному символу. Поэтому первая команда ехрг (46.28) получает три аргумента (79, *, 4 5) и выполняет умножение двух чисел. $ «хрг 79 \* 45 3555 $ ехрг 79 * 45 ехрг: syntax error Во второй команде, которая не содержит обратной косой черты, shell заменяет символ * списком имен файлов, что "смущает" команду ехрг. (Чтобы понять, что имеется в виду, повторите эти два примера, используя команду echo (s.06) вместо ехрг.) • Одинарная кавычка (') отменяет специальное назначение всех символов до следующей одинарной кавычки. Поэтому в следующей командной строке все символы между одинарными кавычками теряют специальное назначение. Сами кавычки удаляются из строки. $ echo Hey! What's next? Mike's #1 friend has 5$. Hey! Whats next? Mikes Рассмотрим более внимательно, что же происходит. Пробелы вне кавычек воспринимаются как разделители аргументов. Интерпретатор shell игнорирует последовательности пробелов. Как указано в параграфе 8.06, команда echo выводит один пробел между аргументами, которые она получает. Внутри кавычек выводится столько пробелов, сколько набрано. Поскольку знак вопроса находится внутри кавычек, он также передается команде echo как обычный символ, а не как специальный. Итак, команда echo выводит свой первый аргумент Неу! и один пробел. Вторым аргументом является Whats next? Mikes. Это единый аргумент, поскольку пробелы находятся между кавычками (обратите внимание, что после знака вопроса выведено два пробела: "? "). В следующем аргументе, #1, первый знак является символом комментария (44.42). Это означает, что shell проигнорирует остальную часть строки и не передаст ее команде echo. • Двойная кавычка (") почти всегда выполняет функции одинарной кавычкиу Разница в том, что двойная кавычка сохраняет особое назначение символов $ (знак доллара), * (обратная кавычка) и \ (обратная косая черта). Это позволяет производить подстановку значений переменных (6.oi, в.ов) и результатов выполнения команд (ч.щ между двойными кавычками, а также отменять такую подстановку. Повторим предыдущий пример. На этот раз поместим одинарные кавычки в двойные (в действительности в двойные кавычки заключена вся строка). $ echo "Hey! What's next? Mike's #1 friend has $$." Hey! What's next? Mike's #1 friend has 18437. Строка начинается с двойной кавычки, которая не встречается до ее конца. Поэтому пробелы между двумя кавычками теряют специальное назначение, и shell передает строку команде echo как один аргумент. Одинарные кавычки также теряют особое назначение, вследствие чего они не удаляются, как в предыдущем примере, а выводятся командой echo. Какие еще символы теряют специальное назначение? Знак #. Обратите внимание, что на этот раз команде echo передается остаток строки, поскольку это уже не комментарий. Но знак доллара ($) не теряет своего назначения, и символы $$ заменяются идентификатором процесса (з&вз) (в данном случае — 18437). интерпретирует команды 123
8.14 Что бы произошло, если бы в предыдущем примере мы заключили символ $ в одинарные кавычки? (Напоминаем, одинарные кавычки отменяют специальное назначение этого символа.) Будет ли shell по-прежнему раскрывать значение символов $$? Да, будет: одинарные кавычки потеряли особое назначение и не влияют более на заключенные в них символы. $ echo "What's next? How many $$ did Mike's friend bring?" What's next? How many 18437 did Mike's friend bring? Как добиться того, чтобы и одинарные кавычки, и знаки доллара выводились буквально? Проще всего воспользоваться обратной косой чертой, которая сохраняет свои функции и между двойными кавычками. $ echo "What's next? How many \$\$ did Mike's friend bring?" What's next? How many $$ did Mike's friend bring? Эту задачу можно решить иначе. Внимательно рассмотрите пример, и вы глубже поймете механизм применения кавычек в shell. $ echo "What's next? How many "'$$'" did Mike's friend bring?" What's next? How many $$ did Mike's friend bring? Чтобы понять этот пример, вспомните, что двойная кавычка отменяет специальное назначение символов, пока не встретится следующая двойная кавычка. Это относится и к одинарным кавычкам. Поэтому строка What's next? How many (включая пробел в конце) находится между двумя кавычками. Символы $$ заключены в одинарные кавычки. Остаток строки окружает вторая пара двойных кавычек. Обе строки в двойных кавычках содержат одинарную кавычку. Двойные кавычки отменяют их особое назначение, вследствие чего одинарные кавычки выводятся буквально. Одинарные кавычки между одинарными кавычками? Одинарные кавычки нельзя помещать между другими одинарными кавычками. Одинарная кавычка отменяет особое назначение всех символов, пока не встретится следующая одинарная кавычка. В подобных случаях используйте двойные кавычки и обратную косую черту. Кавычки в многострочных командах Все символы, которые следуют за одинарной или двойной кавычкой, защищены от обработки интерпретатором shell. В кавычки можно заключить несколько строк. (Интерпретатор С shell не позволяет сделать это.) Так, может показаться, что в небольшом сценарии, представленном на рис. 8.1, символы $1 находятся между кавычками. Это не так. awk ' /fbo/' F { print !$l\ } Рис. 8.1. "Конкурирующие" кавычки На самом деле в кавычки заключено все, кроме $1. (Область в кавычках обозначена серым цветом.) Поэтому символы $1 раскрываются интерпретатором Bourne shell, а не командой awk. Вот еще один пример. Занесем в переменную shell (6.os) многострочное сообщение, которое должно быть помещено в переменную как один аргумент. Внутри двойных кавычек символы $ и интерпретируются (между прочим, до того, как переменная будет загружена). Открывающая двойная кавычка не закрывается в конце первой строки; Bourne shell выводит вторичное приглашение (9.П) (>), пока кавычка не закроется. $ greeting="Hi, $DSER. > The date and time now > axe: 'date1." $ echo "$greeting" Hi, jerry. 124 Часть вторая. Поручите грязную работу компьютеру
8.15 The date and time now are: Tue Sep 1 13:48:12 EDT 1992. $ echo $greeting Hi, jerry. The date and time now are: Tue Sep 1 13:48:12 EDT 1992. $ В строке, где команда echo встречается впервые, использованы двойные кавычки. Это обусловливает подстановку значения переменной. В этой команде shell не использует пробелы и символы новой строки в качестве разделителей аргументов. (Обратите внимание на лишний пробел после слова are:.) В строке, где команда echo используется во второй раз, кавычки не употребляются. Символы пробела и новой строки рассматриваются как разделители аргументов. Shell передает команде echo 14 аргументов, а она выводит эти аргументы, вставляя между ними по одному пробелу. Обратная косая черта имеет особенность, о которой вам следует знать. Если она применяется вне кавычек, в конце строки (непосредственно перед символом новой строки), символ новой строки будет удален. Однако между одинарными кавычками обратная косая черта в конце строки выводится как обычный символ. Приведем примеры. Следующие приглашения пронумерованы (1$, 2$ и т.д.). 1$ echo "a long long long long long long > line or two" a long long long long long long line or two" 2$ echo a long long long long long long\ > line a long long long long long longline 3$ echo a long long long long long long \ > line a long long long long long long line 4$ echo "a long long long long long long\ > line" a long long long long long longline 5$ echo 'a long long long long long long\ > line' a long long long long long long\ line Рассмотрим все по порядку. В примере 1 символ новой строки находится между кавычками, поэтому он не является разделителем аргументов. Команда echo выводит его в составе единственного аргумента (состоящего из двух строк). В примере 2 обратная косая черта перед символом новой строки дает интерпретатору shell указание удалить этот символ, вследствие чего слова long и line передаются команде echo как один аргумент. Пример 3 может быть использован для ввода длинного списка аргументов командной строки. Перед символом обратной косой черты в качестве разделителя аргументов набран пробел. В примере 4 обратная косая черта заключена в двойные кавычки и сохраняет свое специальное назначение (сравните с примером 1). Между одинарными кавычками (пример 5) обратная косая черта не имеет специального назначения и передается команде echo. - JP 8.15 Различия в использовании кавычек между С shell и Bourne shell В этом параграфе описаны различия между С shell и Bourne shell в плане использования кавычек и обратной косой черты. Если вы не прочитали параграф 8.14, пожалуйста, сделайте это. Как и в Bourne shell, в С shell кавычки применяются главным образом для отмены особого назначения специальных символов. Специальные стволы В С shell есть несколько дополнительных специальных символов: !, {, }, ~ fan shell интерпретирует команды 125
8.15 Правила использования кавычек и обратной косой черты Соответствующие правила приведены в табл. 8.2. Они могут понадобиться вам при изучении примеров. Таблица 8.2. Отмена назначения специальных символов в С shell Пример I Действие 'ххх' Отмена специального назначения у всех символов в строке ххх, за исключением символа ! "ххх" Отмена специального назначения у всех символов в строке ххх, за исключением $, ' И ! \х Отмена специального назначения символа х. Символ \, расположенный в конце строки, обеспечивает замену символа новой строки пробелом; таким образом, следующая строка будет продолжением текущей Основные различия в использовании кавычек между С shell и Bourne shell состоят в следующем: • Специальное назначение восклицательного знака (!) может быть отменено только при помощи обратной косой черты. Это остается верным независимо от того, находится восклицательный знак между кавычками (одиночными или двойными) или вне кавычек. Поэтому между кавычками можно выполнять подстановку команд из перечня (ii.07>. Например: % grep intelligent engineering file*.txt grep: engineering: No such file or directory % grep '!:l-2' ! :3 grep 'intelligent engineering' file*.txt • В Bourne shell обратная косая черта (\), заключенная в двойные кавычки, предотвращает подстановку значений переменных и результатов выполнения команд (отменяется специальное назначение символов $ и '). В С shell невозможно отменить особое назначение символов $ и ', которые находятся между двойными кавычками. Необходимо совместно использовать одинарные и двойные кавычки. Например, поиск строки use the ~-c' switch требует определенных усилий: % fgrep "use the \'-c' switch" *.txt Unmatched ". % fgrep 'use the \'-c\' switch' *.txt Unmatched '. % fgrep "use the "'--C"" switch" *.txt hints.txt:Be sure to use the '-c1 switch (В параграфе 10.08 приведена пара симпатичных пользовательских команд, автоматизирующих решение сложных задач, подобных приведенному примеру.) • В Bourne shell в одинарные и двойные кавычки можно помещать символы новой строки. За открывающей кавычкой могут следовать несколько строк, после которых кавычки следует закрыть. Если в командной строке С shell количество открывающих и закрывающих кавычек не совпадает, выводится сообщение об ошибке. Чтобы заключить в кавычки несколько строк, в конце каждой строки необходимо ввести обратную косую черту. Между одинарными или двойными кавычками сочетание символов обратной косой черты и новой строки превращается в символ новой строки. Если это сочетание не заключено в кавычки, оно выполняет функцию разделителя аргументов: % echo "one\ two" three\ four one two three four - JP 126 Часть вторая. Поручите грязную работу компьютеру
8.17 8.16 Отмена действия специальных символов в именах файлов Чтобы применять в работе файлы, имена которых содержат пробелы и специальные символы, нужно использовать кавычки и обратную косую черту. Например, для создания файла с пробелами в имени можно воспользоваться следующей командной строкой: /dev/null 13.14 % ср /dev/null 'имя с пробелами' Обычно интерпретатор shell с помощью пробелов определяет конец каждого аргумента. Кавычки (8.14,8.15) изменяют это правило, поэтому в предыдущем примере есть всего два аргумента. Перед специальным символом можно также поставить обратную косую черту. В следующем примере имя файла с пробелом заменяется именем с символом подчеркивания (_): % mv a\ file a_file Используя ту же методику, можно работать с любым символом в имени файла: % mv '$а' а Применение пробелов в имени файла сопряжено только с одной проблемой: такой файл трудно использовать как аргумент. Другие специальные символы (например, вопросительный знак и звездочку) опасно включать в имя файла. Так, при попытке удаления файла а? можно удалить не только этот файл. - ВВ 8.17 Вывод результатов подстановки специальных символов: переменные verbose и echo С shell имеет две переменные (6.09), которые, если их установить, позволяют проследить замысловатый процесс подстановки значений переменных и метасимволов. Следующая команда обеспечивает вывод каждой командной строки перед определением значения переменных shell. set 6.0S % set verbose А эта команда обеспечит вывод каждой командной строки после подстановки значений переменных и метасимволов. % set echo Если нужно уничтожить эти переменные, вместо команды set используйте команду unset. В Bourne shell синтаксис иной. Чтобы установить флаг verbose, введите следующее: $ set -v Команда set -x устанаачивает флаг echo. Ее можно совместить с предыдущей командой: set -xv. Если ваша версия UNIX поддерживает (44.04) сценарии, начинающиеся символами #!, удобно установить эти переменные в следующей строке сценария: #!/bin/sh -xv Совсем не обязательно изменять программу. Разрешить трассировку переменных в сценариях Bourne shell можно путем указания имени сценария и соответствующих опций в командной строке: $ sh -v сценарий $ sh -х сценарий Не все версии Bourne shell позволяют отменить режим verbose и echo. Если ваш интерпретатор разрешает сделать это, вместо знака - нужно поставить знак +: set +xv - ВВ Как shell интерпретирует команды 127
8.18 8.18 Конструкций "документ здесь' <9) ftpfile До сих пор мы говорили об использовании трех символов: обратной косой черты, одинарной и двойной кавычек. Интерпретаторы shell поддерживают также конструкцию, называемую документ здесь. Эта конструкция удобна, если нужно прочесть какие-либо данные со стандартного ввода и поместить их прямо в сценарий (или в командную строку), но создавать файл для приема этих данных нежелательно. Для этого используйте оператор << с последующим специальным словом: sort >file «EndOfSort zygote abacus EndOfSort Это очень полезный прием, поскольку значения переменных (в.оя, e.oi) определяются во время выполнения команды. Вот как можно передать из сценария файл, используя анонимный ftp-сеанс (52.07): #!/bin/sh # Использование: # ftp-файл компьютер файл # set -х S0URCE=$1 FILE=$2 GETHOST="uname -n" BFILE=~basename $FILE~ ftp -n SSOURCE «EndFTP ascii user anonymous $USER@'$GETHOST~ get $FILE /tmp/$BFILE EndFTP Как видите, происходит подстановка значений переменных и результатов выполнения команд (9.16). Для отмены подстановки поставьте перед соответствующим именем обратную косую черту: cat >file «\FunkyStriNG Обратите внимание: имени переменной FunkyStriNG предшествует обратная косая черта, поскольку я не хочу, чтобы содержащиеся в этой переменной слова попали в какой-либо файл. Кроме того, С shell предполагает, что завершающее слово предваряется таким же символом: \FunkyStriNG. В Bourne shell это требование не выдвигается (см. параграф 45.26). [Большинство версий Bourne shell также имеют оператор «-. Дефис в конце оператора указывает интерпретатору shell удалять все символы табуляции в начале строки. Этот оператор можно использовать для ввода текста с отступами без последующей передачи символов табуляции на стандартное устройство ввода команды. — JP\ - ВВ 8.19 Специальные символы и операторы Прежде чем приступить к изучению регулярных выражений (2t.oi), следует разобраться в том, как выполняется отмена специального назначения символов (t.tt) в UNIX. В регулярных выражениях, как и в интерпретаторе shell, применяются специальные символы (метасимволы). При использовании регулярных выражений в сценариях возникает проблема. Будет ли shell обрабатывать особым образом тот или иной символ или он останется неизменным? Возьмем, например, символ $. Он может находиться как в начале имени переменной, так я в регулярном выражении рь.Ю). Если вы используете регулярное выражение, вам следует знать, содержит ли оно метасимволы, а также обеспечить их правильную обработку. В табл. 8.3 перечислены специальные символы и операторы С shell (csh) и Bourne shell (sh). Для полноты картины приведены также некоторые комбинации символов. Как обычно, то, что относится к sh, применимо к ksh и bash, а то, что касается csh, — к tcsh. 128 Часть вторая. Поручите грязную работу компьютер]/
8.19 Таблица 8.3. Список специальных симвопов и их назначение Симвоя/оператор ESC RETURN пробел TAB TAB # И ft 1 1 \ \ $ переменная ${переменная} ^переменная: модификатор ${переменная-значение_ по_умолчанию} ${переменная=значение по_ у мол ча нию} ${переменная+значение} $ {переменная 7'сообщение} Где испопьзуется csh csh, sh csh, sh csh, sh bash csh, sh csh, sh sh csh sh csh sh csh csh, sh csh, sh csh sh sh sh sh Назначение Дополнение имени файла Выполнение команды Разделитель аргументов Разделитель аргументов Дополнение имени файла Начало комментария Подстановка результатов выполнения команды (обратные кавычки) Отмена специального назначения некоторых специальных символов Отмена специального назначения некоторых специальных символов Отмена специального назначения всех специальных символов Отмена специального назначения всех специальных символов Отмена специального назначения одного специального символа Отмена специального назначения одного специального символа Значение переменной То же, что и ? переменная Редактирование переменной при помощи модификатора Если переменная не определена, использовать значение_по_умолчанию Если переменная не определена, присвоить ей значение_по_умолчанию и использовать его Если переменная определена, использовать значение, иначе — пустую строку Если переменная не определена, вывести сообщение (или сообщение по умолчанию). Если переменная определена, использовать ее значение Параграф 9.08 41.02 8.05 8.05 9.08 44.02 9.16 8.14 8.14, 8.15 8.14 8.14, 8.15 8.14 8.14, 8.15 6.01, 6.08 6.08 9.06 45.12 45.12 45.12 45.12 K«w shell интерпретирует команды S 9-171 129
8.19 Символ/оператор ${строка^шаблон) ${строка^#шаблон) ${строка%шаблон) ${строка%Чшаблон) 1 IS Л л & ? * / / / - -имя пользователя ! — $# "$@" $* $- Где используется ksh, bash ksh, bash ksh, bash ksh, bash ksh, bash csh только sh csh, bash csh, sh csh, sh csh, sh csh, sh sh csh, ksh, bash csh, ksh, bash csh, bash Программы Программы csh, sh sh csh, sh sh Назначение Строка, полученная путем удаления с начала строки наименьшей части строки, совпадающей с шаблоном Строка, полученная путем удаления с начала строки наибольшей части строки. совпадающей с шаблоном Строка, полученная путем удаления с конца строки наименьшей части строки, совпадающей с шаблоном Строка, полученная путем удаления с конца строки наибольшей части строки, совпадающей с шаблоном Канал Переадресация стандартного потока вывода и стандартного потока ошибок для канала Символ канала (устаревший) Редактирование предыдущей командной строки Запуск программы в фоновом режиме Соответствует одному символу Соответствует нескольким или ни одному символу Разделитель команд Конец оператора case Начальный каталог Начальный каталог пользователя с указанным именем Перечень ранее введенных команд Начало необязательного аргумента Чтение стандартного ввода (только в некоторых программах) Количество аргументов сценария Аргументы сценария как одна строка Набор аргументов сценария Установленные флаги shell Параграф 9.07 9.07 - 9.07 9.07 1.04, 13.01 13.05 11.05 1.27, 1.28 1.16, 15.02 1.16, 15.02 8.05 44.05 14.11 14.11 11.02 8.05 13.13 44.15 44.15 44.15 2.11 130 Часть вторая. Поручите грязную работу компьютеру
8.19 Символ/оператор ?? ?$. $! $< команда1 && команда2 команда 1 | | команда2 $(..) ((..)) . файл '• [] [] ^задание (команда1;команда2) О {команда1;команда2; } >файя »файл <файл «слово Где используется sh csh, sh sh csh csh, sh csh, sh ksh, bash ksh, bash sh sh sh csh csh, sh sh csh, ksh, bash csh, sh csh, bash sh csh, sh csh, sh csh, sh csh, sh Назначение Код завершения предыдущей команды Идентификатор процесса Идентификатор процесса последнего фонового задания Чтение ввода с терминала Выполнение команды2, если команда 1 завершена успешно Выполнение команды2, если команда! завершена неудачно Подстановка результатов выполнения команды Арифметическое вычисление Выполнение в текущем интерпретаторе shell команд из файла Определение значений аргументов (код возврата равен true) Разделитель аргументов в переменной PATH Модификатор переменней Диапазон символов Условная конструкция Номер задания Выполнение команд в порожденном интерпретаторе shell Подстановка выражения То же, что и конструкция (кома нда 1; кома нда 2), но без запуска порожденного интерпретатора shell Переадресация стандартного вывода Добавление стандартного вывода Переадресация стандартного ввода Чтение стандартного ввода, пока не встретится слово, выполнение команды и подстановка значений переменных Параграф 44.07 8.14 7.12 9.13 44.09 44.09 9.16, 45.31 44.23 45.09 6.04, 14.05, 21.08 9.06 1.16, 15.02 44.20 12.01 13.07 9.05 13.07 13.01 13.01 13.01 8.18, 9.14 f«r shell интерпретирует команды 5* 131
8.19 Симвоп/опвратор «\слово «-слово >>! файл >! файл >| файл >& файл т> файл т> > файл т< файл <&т <&- >&т >&- т<£п т<&- п>£т т>&- Где используется csh, sh sh csh csh ksh, bash csh sh sh sh sh sh sh sh sh sh sh sh Назначение Чтение стандартного ввода, пока не встретится слово. Подстановка не выполняется Чтение стандартного ввода, пока не встретится слово. Начальные символы табуляции игнорируются Добавление стандартного вывода к файлу, даже если установлена переменная noclobber, а файла не существует Вывод в файл, даже если установлена переменная noclobber, а файл существует Вывод в файл, даже если установлена переменная noclobber, а файл существует Переадресация стандартного вывода и стандартного потока ошибок в файл Переадресация вывода файла с дескриптором m в файл Добавление вывода из файла с дескриптором m к файлу Переадресация ввода в файл с дескриптором m из файла Стандартный ввод из файла с дескриптором m Закрытие файла стандартного ввода Стандартный вывод в файл с дескриптором m Закрытие файла стандартного вывода Переадресация ввода в файл с дескриптором m из файла с дескриптором п Закрытие файла ввода с дескриптором m Переадресация вывода из файла с дескриптором п в файл с дескриптором m Закрытие файла вывода с дескриптором m Параграф 8.18 8.18 • 13.06 13.06 13.06 13.05 45.21 45.10 45.21 45.21 45.22 45.21 45.21 45.21 - ВВ, JP 132 Часть вторая. Поручите грязную работу компьютеру
8.20 8.20 Сколько нужно символов обратной косой черты? Во многих программах символ обратной косой черты предназначен для отмены специального назначения символов, что может стать причиной проблем при его использовании. Иногда трудно определить, сколько таких символов нужно употребить в той или иной ситуации. Приведем пример из System V Release 4. (Обратите внимание: используется стандартная для System V версия команды echo из каталога /bin. В общей сложности в этой системе насчитывается четыре версии команды echo.) % /bin/echo hi \ there hi there % /bin/echo hi \\ there hi \ there % /bin/echo hi \\\\ there hi \ there В первом случае интерпретатор shell использует обратную косую черту для "защиты" следующего символа пробела. Пробел перед обратной косой чертой является разделителем слов. Поэтому команда echo получает два аргумента: hi и Gthere (без обратной косой черты), где символ П является обозначением "защищенного" пробела. Команда echo выводит пробел между двумя аргументами. Первый пробел в выходной строке служит для разделения аргументов команды echo, а второй добавляется вместе со вторым аргументом (благодаря обратной косой черте). Во втором случае shell преобразует \\ в \, поскольку первая косая черта указывает интерпретатору shell отменить специальное назначение (s.u) следующего символа — обратной косой черты. Команда echo получает три аргумента — hi, \ и there — и выводит их, разделяя пробелами. (Мне приходилось слышать жалобы на то, что в System V Release 4 команда echo никогда не выводит символов обратной косой черты, но мне не удалось воссоздать подобную ситуацию.) В третьем случае интерпретатор shell преобразует каждую пару символов обратной косой черты в один символ и запускает команду hi \\ there. Команда echo, принадлежащая System V, интерпретирует символы обратной косой черты как специальные г*ад. Поэтому, обнаружив две другие обратные косые черты, она преобразует их в один символ. Вот почему мы видим только одну косую черту, хотя ввели четыре. В системах типа BSD команда echo не ведет себя подобным образом, и мы увидим две косые черты. Встроенная в С shell системы SVR4 команда echo работает так же, как и ее аналог в BSD. Это относится и к команде /usr/ucb/echo в SVR4. Драйвер терминала (шо также может удалить символы обратной косой черты, когда они расположены перед специальными символами. Если обратная косая черта предшествует команде удаления предыдущего символа (обычно [CTRL-h]) или строки (обычно [CTRL-u]), терминал не воспринимает эти символы как команды редактирования и передает их интерпретатору shell. По ходу дела он удаляет символ обратной косой черты. Если ввести строку: % echo \ICTRL-al интерпретатор shell получит строку echo CTRL-u. Для получения справочной информации просмотрите страницу termio руководства, где отмечены системные особенности. Символ обратной косой черты используются и драйвером терминала, и командой echo (иногда), и некоторыми другими утилитами. Если все тщательно проанализировать, то можно понять, куда делись эти символы. Если вы не склонны проводить анализ, добавляйте символы обратной косой черты, пока не получите желаемый результат. (Учтите: работая наугад, вы можете попасть в ловушку.) Мне знакома ситуация, когда в редакторе troff дз.п) (который представляет собой отдельную тему для разговора) пришлось вставить восемь символов обратной косой черты, чтобы получить один! (Упражнение; что произойдет, если заключить в кавычки (" или ') строки, переданные команде echo в предыдущих примерах? Повлияют ли кавычки на способ интерпретации строки \ICTRL-ul ?) - ML, JP Как shell интерпретирует команды 133
9 Как сэкономить время при вводе командной строки 9.01 Что нужно знать о командной строке UNIX Самое прекрасное в UNIX — командная строка. Почему? Все современные операционные системы имеют командную строку. Сегодня уже не используются перфокарточные считыватели с загадочными карточками инициализации задания. Что же особенного в UNIX? Эта система позволяет применять в командной строке множество сокращений. Только некоторые из них используются в других операционных системах. В этой главе мы рассмотрим многие сокращения, а также обсудим следующие вопросы: • Быстрое исправление (я.о2) ошибок с помощью команд удаления символа и строки. (Эти команды присущи не только интерпретатору shell, и кроме строки приглашения они работают во многих программах.) • Дополнение имени файла (я.о8, я.оя, я.юу. вы вводите начало имени файла, a shell добавляет остальную часть. • Подстановка результатов выполнения команды (я.ну. результат выполнения одной команды используется в качестве аргумента другой команды (что отличается от механизма создания конвейера рм).) • Подстановка процесса (я.щ в bash и в других интерпретаторах. В частности, мы рассмотрим сценарий с именем !, позволяющий помещать выходные данные команды во временный файл и передавать имя этого файла процессу. • Досрочный ввод (я.щ: возможность ввода следующей команды (или команд), в то время как предыдущая команда еще выполняется. • Работа с очень длинными командными строками (9.2а, я.21, я.23). Ниже перечислены некоторые возможности интерпретаторов, рассмотренные в других главах: • Управление заданиями p2.oiy. возможность одновременного выполнения нескольких команд. • Использование псевдонимов (io.o2), или сокращенных имен различных команд, а также функций shell (Ю.ояу • Редактирование командной строки риз) и подстановка команд из перечня pi.ту. два различных способа повторного вызова предыдущих команд. • Использование кавычек и обратной косой черты (8.14,8.15) в качестве средств "защиты" специальных символов от интерпретатора shell. • Использование метасимволов ps.m). Чтобы эффективно использовать UNIX, не нужно быть виртуозом в создании командной строки. Однако вы много теряете, если все, что вы можете делать в командной строке, — это ввод команды Is или запуск профаммы FrameMaker. При помощи всего нескольких трюков можно добиться поразительных результатов. - ML 134 Часть вторая. Поручите грязную работу компьютеру
9.03 9.02 Быстрое редактирование командной строки Удивительно, насколько часто даже опытные пользователи UNIX упорно нажимают клавишу [BACKSPACE] или [DELETE], чтобы удалить почти готовую командную строку, которая может содержать всего одну ошибку. Гораздо проще ввести команду удаления строки, например [CTRL-u] или [CTRL-x]. (Команда stty -a или stty everything (и.оз) указывает, какое сочетание клавиш используется, а в параграфе 5.09 показано, как назначить этим командам другие сочетания клавиш.) Команда удаления строки работает в командной строке (по приглашению sheU p.oi)), а также в некоторых программах, когда терминал находится в режиме обработки данных (И.ю). Программы UNIX, которые не работают в режиме обработки данных (например, vi), также "понимают" команду удаления строки. В некоторых системах есть команда удаления слова (обычно [CTRL-w]), которая удаляет символы до предыдущего пробела. Нет смысла удалять всю командную строку, если нужно изменить только ее часть! В некоторых системах, имеющих механизм редактирования командной строки (плз), команды для удаления строки и слова могут работать не так, как мы описали. Причина заключается в том, что редактирование командной строки выполняется не в режиме обработки данных. При этом shell обрабатывает каждый введенный вами символ. Загляните в руководство по своему интерпретатору shell. - JP, TOR 9.03 Повторный ввод командной строки Представьте, что вы вошли в систему с домашнего компьютера и в ответ на приглашение запустили программу. Когда почти все сделано, модем из-за помех выводит на экран сообщения типа xDxD@ !. Кроме того, возможна следующая ситуация: вы набираете длинную команду, а товарищ прерывает вас с помощью утилиты write (1.зз), сообщая, что пора обедать. Нужно ли в подобных случаях нажимать клавиши [CTRL-ul (9.02) и начинать ввод сначала? Если ваша система понимает команду повторного вывода (обычно этой команде соответствует сочетание клавиш [CTRL-r]), можете задать повторный вывод командной строки в том виде, который она имела в тот момент, когда ввод был прерван. Команду [CTRL-r] можно ввести не только после прерывания ввода, но и в любое время, чтобы узнать, как восприняла система вашу командную строку. Указанная команда работает, если терминал находится в режиме обработки данных ai.oy. Программы типа vi, которые сами обрабатывают ввод, могут иначе воспринимать команду [CTRL-r]. Приведем пример: % egrep '(10394|29433|49401)• /work/symtower/ Message from alison@ruby on ttyp2 at 12:02 ... how about lunch? EOF БтБь^ egrep ' (10394 I 29433|49401) ' /work/symtower/logs/* После прерывания ввода я нажал клавиши [CTRL-r]. В результате была выведена строка, которую я начал набирать. Я завершил ввод строки и нажал клавишу [RETURN], чтобы выполнить ее. Если вы работаете с Korn shell, в котором поддерживается интерактивное редактирование командной строки, сочетание клавиш [CTRL-r] можно использовать для повторного вывода введенной строки. В bash при редактировании командной строки в режиме vi команда [CTRL-r] запускает обратный поиск в стиле редактора Emacs. Я исправил эту ошибку в своем файле /.inputre: (f По умолчанию "R запускает процедуру поиска "reverce-i-search" # даже при отсутствии введенного текста! Устранение: "\С-г: redraw-current-line - JP Как сэкономить время при вводе командной строки 135
9.04 9.04 Метасимволы и создание файлов Специальные символы shell [ ] (квадратные скобки) соответствуют диапазону имен файлов. Допустим, у вас есть файлы aflle, bfile, cflle и aflle. Чтобы вывести на печать первые три, следует набрать такую команду: % lpr [a-c]file А теперь предположим, что нам необходимо создать дополнительные файлы с именами efile, ffile, gfile и hflle. Что неправильно в приведенной ниже командной строке? Попробуйте ее" выполнить. Вместо редактора vi можете использовать свой любимый редактор или команду touch (21.07). % vi [e-h]file Указанные файлы не создаются % Is afile bfile cfile dfile Удивлены? Прочтите параграф 1.16, в котором говорится о метасимволах. Ответ таков: метасимволы не могут соответствовать именам несуществующих файлов. В первую очередь это касается команд touch ?file (21.07) и touch *file: подумайте только, сколько файлов можно было бы создать посредством этих команд. В параграфе 9.05 описан оператор shell {}, который позволяет решить данную задачу. - JP 9.05 Создание строк с помощью оператора О Я нашел множество применений специальным символам {} при подстановке символов по шаблону в csh, tcsh и bash. (В других интерпретаторах оператор {} также используется; см. параграф 15.03.) По функциональному признаку эти специальные символы подобны символам *, ?и [] (15.02), однако, в отличие от других метасимволов, они не задают образец для поиска файлов. Оператору {} можно передать любой текст (а не только имена файлов). Способность этого оператора обрабатывать "все что угодно" делает его особенно полезным. Приведем несколько примеров: • Исправление ошибки в имени файла (замена fixboldS.c на fixbold6.c): % mv fixboid{5,6).с Добавив команду echo (S.06) перед командой mv, несложно проверить, что выполняет интерпретатор shell с помощью фигурных скобок: % echo mv fixbold{5,6}.c mv fixbold5.c fixbold6.c • Очень легко скопировать файл в файл.Ьак: % ср файл{,.Ьак} • Возможна печать файлов из других каталогов без повторного набора полного путевого имени: % lpr /usr3/hannah/training/{ed,vi,mail}/lab.{merout} Эта строка обеспечит единовременную передачу всех необходимых файлов команде l£r (43.шу. /usr3/hannah/training/ed/lab.газ /usr3/hannah/training/ed/lab.out /usr3/hannah/training/vi/lab.ms /usr3/hannah/training/vi/lab.out /usr3/hannah/training/mail/lab.ms /usr3/hannah/training/mail/lab.out • Создание десяти новых файлов: % vi /usr/foo/file{a,b,c,d,e,f,g,h,i,j) В результате будут созданы файлы /usr/foo/filea, /usr/foo/flleb, ... /usr/foo/filej. Поскольку в момент активизации команды файлы не существуют, метасимволы в команде vi /usr/foo/file[a-j ] не работают (ям). 136 Часть вторая. Поручите грязную работу компьютеру
9.06 Очень просто перебрать все трехразрядные числа от 000 до 299, выполнив следующую команду: foresach n ({0,1,2}{0,1, 2, 3, 4, 5, 6, 7, 8, 9} {0,1, 2, 3, 4, 5, 6, 7, 8, 9}) ... Обработка числа $п end Конечно, csh имеет встроенные арифметические команды, но его оператор @ (47.04) не может создавать числа с ведущими нулями. Этот симпатичный пример показывает, что операторы {} хороши не только для обработки имен файлов. • Создание набора подкаталогов: % mkdir man % mkdir man/{man,cat}{1,2,3,4,5, 6,7, В} % Is -F man catl/ cat3/ cat5/ cat7/ manl/ тапЗ/ тап5/ тап7 cat2/ cat4/ cat6/ cat8/ man2/ man4/ тапб/ man8/ • Распечатка файла project_report в десяти экземплярах (если ваша версия команды Ijr (4.1.02) не поддерживает опцию -#10): % lpr proj*ct_repor{t,t,t,t,t,t,t,t,t,t) -т JP 9.06 Операторы редактирования строк С shell и bash одновременно с подстановкой команд из перечня (пят) могут редактировать подставляемые команды. С shell способен также редактировать подставляемые значения переменных (б.оя), что невозможно в bash. Так, в первом примере (см. ниже) добавление оператора :h приведет к выделению только начала имени файла (/а/Ь), если переменная !$ содержит /а/Ь/с. Полный, но немногословный перечень этих операторов приведен в руководстве по csh. Мы надеемся, что следующие примеры помогут освоить функции этих полезных операторов. • Оператор :h выделяет имя каталога из полного путевого имени ош), например: % echo /а/Ь/с а/Ь/с % echo !$:h echo /a/b /a/b Оператор :h отсекает имя файла и оставляет только имя каталога. Этот оператор работает аналогичным образом, когда путевое имя занесено в переменную интерпретатора С shell (47.0.V: % set x = /а/Ь/с % echo $x а/Ь/с % echo $x:h /а/Ь • Оператор : г возвращает имя файла без расширения: % echo xyz.с аЬс.с xyz. с abc.c % echo !$:г echo abc abc Оператор : г удалил символы . с из последнего аргумента, оставив только имя файла. То же можно сделать с именами переменных С shell: % set x = abc.с % echo $x:r abc (breach 9.11 К» сэкономить время при вводе командной строки 137
9.06 • Оператор :g предназначен для работы с несколькими именами. При его добавлении операция становится глобальной. Например: (..:) 47.05 % set х х (а.а Ь.Ь с. с) % echo $x:gr a b с Оператор :gr удалил все суффиксы с точкой. Между прочим, такой способ использования оператора : g не подходит для перечня ранее введенных команд. Последняя строка — это реакция С shell на команду baseline (4S.is). • Оператор : е возвращает расширение (часть имени после точки). Вот пример его использования с переменными С shell: % set x * (abc.c) % echo $x:e с Однако с перечнем введенных команд этот'оператор не работает. • Оператор :t возвращает окончание путевого.имени — имя файла без пути: % echo /а/Ь/с а/Ь/с % echo !$:t с Этот оператор работает аналогичным образом с переменными csh: % set x * (/а/ь/с) % acho $x:t с То же можно выполнить, задействовав несколько путевых имен: % eet х = (/а/Ь/с /d/e/f /g/h/i) % echo $x:gt с f i Соответствующие заголовки можно получить таким образом: % set х - (/а/Ь/с /d/e/f /g/h/i) % echo $x:gh /а/Ь /d/e /g/h • Оператор :р выводит командную строку, но не выполняет ее о ив/. % echo * fnl fn2 fn3 % ! :p echo fnl fn2 £n3 • Оператор :q предотвращает подстановку имени файла, т.е. выводит командную строку без изменений: % «cho * fnl fn2 fn3 % !:q echo * * Первая команда выводит имена файлов текущего каталога. После применения оператора :q выводится только специальный символ. • Оператор : х подобен оператору : q, но разбивает строку на слова. [Операторы : q и : х чаще используются с массивами С shell (47.05). — JF\ - DR 138 Часть вторая. Поручите грязную работу компьютеру'
9.07 9.07 Редактирование строковых переменных в ksh и bash В С shell операторы редактирования строк (я.ов) можно применять к переменным shell, а в некоторых случаях — для работы с перечнем ранее введенных команд. Для обработки перечня эти операторы используются и в bash. В Korn shell и bash переменные shell редактируются иначе. Операторы редактирования строк перечислены в табл. 9.1.* Таблице 9.1. Операторы редактирования строк в ksh я bash Оператор Действие $ {переменная^ шаблон) ${переменная#§шаблон] ${переменнаяЧшаблон] ${переменная%%шаблон] Удаляет из переменной самую короткую начальную часть, совпадающую с шаблоном. Возвращает остаток Удаляет из переменной самую длинную начальную часть, совпадающую с шаблоном. Возвращает остаток Удаляет из переменной самую короткую конечную часть, совпадающую с шаблоном. Возвращает остаток Удаляет из переменной самую длинную конечную часть, совпадающую с шаблоном. Возвращает остаток Строка шаблон может состоять из метасимволов *, ? и []. В процессе редактирования переменных метасимволы заменяются в строках точно так же, как и в именах файлов. (Это не похоже на регулярные выражения, используемые в редакторе sed.) Два первых оператора, с символом #, служат для редактирования строки с самого начала, а другие два, с символом %, — с конца. Запомнить, где какой символ применять, позволяет следующее правило: знак номера (#) ставится перед числом, а знак процента (%) — после числа. Перейдем к примерам. Переменная var содержит строку /a/b/c/d/e.fg. Выражение ${var} ${var#/*/} ${var##/*/} ${var%.*} ${var%%.*} ${var%%/*/} ${var%%/*} ${var%b*} ${var%%b*} Результат /a/b/c/d/e.f.g b/c/d/e.f.g e.f.g /a/b/c/d/e.f /a/b/c/d/e /a/b/c/d/e.f.g /a /a Приведем еще один пример. Переменная PATH (б.<н> содержит строку, разделенную двоеточиями (:). Предположим, необходимо удалить из системного пути последний каталог и указать вместо него каталог $HOME/bin. Для этого в файл .profile следует ввести следующую команду: РАТН=${РАТН%:*}:$HOME/bin Поскольку выражение ${РАТН%:*} содержит один символ процента, оператор {} удаляет минимум данных: последнее двоеточие и имя каталога после него. По завершении редактирования строка содержит присоединенную строку $НОМЕ/Мп. Новое значение сохраняется в переменной PATH. Операторы подстановки параметров в Bourne shell as.n) выглядят так же, но чаще используются в сценариях. - JP Переменные shell являются строковыми. — Примеч. ред. Как сэкономить время при вводе командной строки 139
9.08 9M Ускорение ввода имен файлов путем их дополнения Если вы не любите использовать длинные имена файлов, то, скорее всего, знаете о возможности sliell дополнять имена файлов. В каждом интерпретаторе эта функция имеет свои особенности, поэтому далее приведен обзор этих особенностей. Подробности можно найти в соответствующем руководстве по интерпретатору shell. ' Можно набрать начальную часть имени файла, а затем нажать клавишу [TAB] (в С shell — клавишу [ESC]). Если shell в состоянии определить полное имя на основании* введенной части, то он добавит оставшуюся часть. Иначе будет добавлена та часть имени, которая не вызывает сомнений, после чего вам предложат набрать остальное. (Чтобы сделать доступной эту функцию, в большинстве версий С shell нужно установить переменную filec (6.09) или complete. В более старых версиях С shell данная функция отсутствует.) Например: S Is alpha.с alpha.о beta.с $ СС bfTABl $ ее beta.с shell автоматически дополняет имя (В tcsh и csh терминал выдает звуковой сигнал, если введенной вами части соответствуют несколько имен файлов. Отменить звуковой сигнал позволяет переменная shell nobeep.) В нашем примере только одно имя файла начинается с буквы Ь, поэтому shell полностью дополнит имя. Если ввести часть имени файла и нажать клавиши [CTRL-d] (в bash — дважды), интерпретатор shell отобразит список всех файлов, имена которых соответствуют заданному критерию. Затем снова будет выведена командная строка, и вы сможете продолжить ввод. Например: % ее alCTRL-dl alpha.с alpha.о % ее alpha. С буквы а начинаются имена двух файлов. Интерпретатор shell выведет эти имена. Затем будет повторно выведена строка с командой ее, что позволит завершить ввод имени файла. Примечание: Нужно иметь в виду, что функция дополнения имен файлов является неофициальной и не всегда работает корректно. Не пытайтесь сочетать дополнение имен файлов с использованием метасимволов, так как это работать не будет. В параграфе 14.09 описана интересная возможность дополнения имен файлов: переход в каталог путем ввода его "инициалов". - ML, JP 9.09 Ненужные имена при дополнении имен файлов Переменная shell /ignore (FIGNORE в bash) позволяет сообщить интерпретатору, что при дополнении имен файлов (я.м) некоторые имена не представляют интереса. Например, вас могут интересовать исходные файлы программ на языке С (имена которых содержат расширение .с), но не объектные файлы (с расширением .о). Исходные тексты часто подвергаются редактированию, а объектные модули просматривать не нужно. Установите в качестве значения переменной flgnore суффикс, который надлежит проигнорировать. Например, чтобы проигнорировать файлы с расширением .о в tcsh и csh, введите следующее: set б.оа % set fignore=(.o) () 47.05 В результате после нажатия клавиши [TAB] (в csh — [ESC]) будут проигнорированы имена с расширением .о, даже если такой файл будет единственным подходящим. 140 Часть вторая. Поручите грязную работу компьютеру
9.11 Скорее всего, найдется целый ряд суффиксов, которые не нужно принимать во внимание: .о (объектные модули), .out (исполняемые файлы), . gz (сжатые файлы) и т.д. Список этих суффиксов приведен в параграфе 1.17. Вот как следует определить переменную /Ignore, чтобы игнорировался список расширений имен файлов: % set fignore=(.o .out .gi) ...tcsh, csh $ FIGNORE='.o:.out:.gi' ...bash В csh и tcsh переменная /ignore не играет никакой роли при выводе списка подходящих файлов по нажатию клавиш [CTRL-d]. В этих интерпретаторах всегда выводится полный список возможных дополнений имен файлов. - ML, JP 9.10 Когда не следует дополнять имя файла? Во многих случаях дополнение имени файла (9.os, п.В) является нецелесообразным: • Если командная строка должна содержать много имен файлов, возможно, следует подобрать выражение с метасимволами (i.i6). • . Как указывалось, нельзя совмещать дополнение имен файлов и применение метасимволов. Например, нужно выполнить отбор файлов на основании расширения имени. При наличии множества файлов, имена которых начинаются с буквы а, проще ввести *.с или а*.с, чем выполнять дополнение имен файлов. • Дополнять имена файлов, наверное, нецелесообразно, если для выбора нужного файла необходимо набрать очень много имен. Так, если имеется множество файлов с похожими именами, дополнение имен файлов вряд ли позволит выбрать один. (Подобная ситуация является следствием неправильного подхода к наименованию файлов. Выходом из нее может быть создание продуманной системы имен.) - ML, JP 9.11 Повторное выполнение команд в цикле foreach Когда нужно применить некоторую команду к нескольким файлам, в первую очередь вспоминается перечень ранее введенных команд (U.osy. ■ -v2S07 % cat -t -v /usr/fran/report | pg % Afran/reportArob/file3 _..... cat -t -v /usr/rob/file3 | pg -: - % A3A2l cat -t -v /usr/rob/file21 I pg % Более простой способ решения подобной задачи состоит в использовании цикла foreach в С shell (в Bourne shell и Кот shell — цикл a./or fl.nf). Оператору цикла предоставляется список аргументов, которые будут обрабатываться при каждом выполнении командной строки. В данном случае таковым является список имен файлов. При выполнении цикла из списка последовательно выбираются аргументы. Каждый аргумент сохраняется в переменной shell (б.щ. После этого выполняется команда (или несколько команд). Цикл повторяется, пока не будут обработаны все аргументы. Например: % foreach file (/usr/fran/report /usr/rob/file3 /usr/rob/file21) ? cat -t -v 'file | pg ? end ...Выполнение команды cat -t -v /usr/fran/report I pg... ... .Выполнение команды cat -t -v /usr/rob/fИеЗ \ pg... ...Выполнение команды cat -t -v /usr/rob/file21 \ pg... % Как сэкономить время при вводе командной строки 141
9.12 Вопросительные знаки являются вторичными приглашениями (9.U). С shell выводит их, пока не будет введена команда end. После этого цикл выполняется. Список в скобках может не содержать имена файлов. В скобки можно также заключить метасимволы (i.iq, обратные кавычки (9.щ (подстановка результатов выполнения команды), переменные (вм, б.оо и очень удобные операторы фигурных скобок ({}) (9.os), имеющиеся в С shell. Так, предыдущий пример мог бы иметь следующий вид: % foreach file (/usr/fran/report /usr/rob/file{3,21}) ? cat -t -v $file | pg ? and Если В каждом цикле нужно сделать паузу до или после выполнения команд, добавьте оператор С shell $<. Этот оператор читает ввод с клавиатуры и ждет нажатия клавиши [RETURN]. В данном случае можно ничего не вводить, а использовать оператор $<, позволяющий сделать паузу в цикле. Например, чтобы в цикле перед выполнением каждой командной строки выводилось сообщение, выполните следующее: % foreach file (/usr/fran/report /usr/rob/file{3,21}) ? echo -n "Press RETURN to see $file—" №t6M 1 set x="$<" ? cat -t -v $file | pg ? end Press RETURN to see /usr/fran/report—I RETURN! ...Выполнение команды cat -t ~v /usr/fran/report I pg... Press RETURN to see /usr/rob/file3—1ЙЕТПНН1 ...Выполнение команды cat -t -v /usr/rob/file3 I pg... Press RETURN to see /usr/rob/file21—IM'fUkHI ...Выполнение команды cat -t -v /usr/rob/file21 I pg... Параметры цикла могут не быть именами файлов. Вот как, например, можно послать персональные почтовые о.зз) сообщения пяти адресатам:* % foreach person (John Cathy Agnes Brett Elma) at.-13.13 ? echo "Dear $person," I cat - formletter | mail $person ? end Первая строка первого письма будет такой: Dear John,; второго письма — Dear Cathy, и т.д. Где еще можно воплотить эту идею? Вопрос относится к области shell-программирования (44.01). Обычно я не рекомендую (47.02) программировать в С shell, но описанный прием очень удобен в интерактивном режиме. - JP 9.12 Оператор цикла for Оператор цикла for в Bourne shell похож на оператор foreach (9.11) в С shell: он обрабатывает список аргументов, выполняя по отношению к каждому из них одну или несколько команд. В таком случае расход времени гораздо меньше, чем при последовательном запуске команд обработки файлов. Повторим пример с оператором foreach из предыдущего параграфа: $ for file in /uer/fran/report /usr/rob/file2 /usr/rob/file3 > do > cat -t -v $file | pg > done ...Выполнение команды cat -t -v /usr/fran/report | pg... * Отправляя множество почтовых сообщений в цикле, вы можете перегрузить системную почтовую программу. В таком случае в отдельной строке следует поместить перед оператором end такую команду, как sleep 5 (мм). Это Предоставит почтовой программе 5 секунд на отправку каждого сообщения. 142 Часть вторая. Поручите грязную работу компьютера
9>13 ...Выполнение команды cat -t -v /usr/rob/file2 | pg... ...Выполнение команды cat -t -v /usr/rob/file3 | pg... $ Правые угловые скобки являются вторичным приглашением (я.и). Bourne shell выводит их, пока не будет введена команда done. Вслед за этим выполняется цикл. После ввода оператора do не обязательно нажимать [RETURN], можно сразу вводить первую команду в той же строке. В сценариях тело цикла (строки между операторами do и done) вводится с отступом. Список после оператора in может не содержать имена файлов. В нем могут быть также обратные кавычки (9.щ (подстановка результатов выполнения команды), переменные (б.яя,б.ог) и метасимволы (is.oi). Так, предыдущий пример мог бы иметь следующий вид: $ for file in /usr/fran/report /usr/rob/fiie[23] > do cat -t -v $file | pg > done Если в каждом цикле до или после выполнения команд необходимо сделать паузу, добавьте команду Bourne shell read (44.1.1). Эта команда читает ввод с клавиатуры и ждет нажатия клавиши [RETURN]. В данном случае можно ничего не вводить, а воспользоваться командой read, позволяющей сделать паузу в цикле. Например, чтобы в цикле перед выполнением каждой командной строки выводилось приглашение, выполните следующее: $ for file in /uer/fran/report /ust/rob/file[23] > do > echo -n "Press RETURN to See $file—" > read x > cat -t -v $file | pg > done Press RETURN to see /usr/fran/report—IRETUHNI ...Выполнение команды cat -t -v /usr/fran/report I pg... Press RETURN to see /usr/rob/file2—ЕЕШЁН1 ...Выполнение команды cat -t -v /usr/rob/ftle2 \ pg... Press RETURN to see /usr/rob/file3—IRETURNI ...Выполнение команды cat -t -v /usr/rob/file3 I pg... Параграф 44.16 содержит дополнительную информацию о цикле for. В параграфе 45.16 показано, как в цикле for обеспечить чтение стандартного ввода вместо списка аргументов -JP 9.13 Многострочные команды и вторичные приглашения Как Bourne shell, так и С shell допускают ввод многострочных команд. В Bourne shell символ новой строки после открывающей кавычки (' или "), символ канала (|) или обратной косой черты (\) не вызывают выполнения команды. Вместо этого выводится вторичное приглашение (из переменной среды PS2, значение которой по умолчанию — >), указывающее, что можно продолжить ввод команды со следующей строки. Например, чтобы отправить срочное сообщение при помощи утилиты write а.зз), не заставляя другого пользователя ждать, пока вы наберете это сообщение., введите следующие строки: $ echo "We're leeving in 10 minutes. Sae you downstairs." | > write Joanne В С shell можно продолжить строку, поставив символ обратной косой черты перед символом новой строки (8.15). При этом вторичное приглашение не выводится. Это удобно, например, при вводе длинной командной строки. Этот "скромный" оператор легко не заметить, однако он существенно упрощает работу в командной строке с такой программой, как sed (34.24). Например, если вы хронически набираете mvoe вместо move и thier вместо their, то, возможно, оцените следующую команду: $ sed > s/mvoe/move/g ЛГ<1р«:«2 > s/1-hier/their/g' myfile | nroff -ms | lp Как сэкономить время при вводе командной строки 143
9.14 Важнее то, что благодаря возможности ввода многострочных команд перед вами открываются пути использования средств программирования в командной строке. И в Bourne shell, и в С shell при вводе многострочных программных конструкций автоматически генерируется вторичное приглашение (в Bourne shell — >, в С shell — ?), пока конструкция не будет завершена. Вот как, например, можнб использовать мою любимую программную конструкцию для непрограммистов — цикл for: $ for x in filel file2 file3 > do > sed 's/thier/their/g' $x > ,$x > mv ,$x $x > done $ Применение цикла foreach (9.ii) в С shell выглядит следующим образом: % foreach x (filel file2 file3) ? sed 's/thier/their/g' $x > ,$x ? mv ,$x $x ? end % Хотя простые команды, подобные этой, можно сохранить в сценарии (t.os), иногда их проще вводить в интерактивном режиме. Пользователям редактора sed обязательно нужно проверить правильность работы сценария, прежде чем изменять исходные файлы (З4.аз). - TOR 9.14 Использование конструкции "документ здесь" для создания шаблонов писем Оператор <£ (8.18) применяется в сценариях. Он указывает интерпретатору shell использовать строки сценария в качестве стандартного ввода команды. В следующем примере показан цикл (45.П), который с помощью команды Ipr (43.02) выводит на печать три шаблона письма- предупреждения. В верхней части каждого письма указываются новое имя и текущая дата. Этот цикл можно поместить в сценарий (44.02) или ввести по приглашению Bourne shell (9.12,9.13). Каждая строка в теле цикла начинается с символа [TAB], который удаляется оператором «- перед выводом текста на печать. for person in "Mary Smith" "Doug Jones" "Alison Eddy" do lpr «- ENDMSG 'date' Dear $person This is your last notice. Buy me pizza tonight or else I'll type "rm -r *" when you're not looking. This is not a joak. Signed, The midnight skulker ENDMSG done 144 Часть вторая. Поручите грязную работу компьютер]/
9.16 j*^/. В этом цикле выполняются три команды Ipr. Каждый вариант письма печатается на отдельной Skk7^ странице. Shell читает стандартный ввод, пока не встречает признак конца ввода, которым Щ0 в данном случае является слово ENDMSG. Это слово должно быть единственным в строке. [45.28] (В некоторых интерпретаторах Bourne shell отсутствует оператор <<- для удаления начальных символов табуляции. В этом случае используйте оператор « и не делайте отступов в теле цикла.) Обратные кавычки (9.U) обеспечивают выполнение команды date (51.10) и получение текущей даты. Переменная Sperson заменяется именем адресата, заданным перед началом цикла. Остальной текст в неизменном виде копируется в стандартный вывод команды Ipr. - JP 9.15 Временные сценарии для сложных команд [Если в вашем интерпретаторе shell поддерживается функция редактирования командной строки, как, например, в Кот shell (li.U), описанная технология все равно окажется полезной. Как говорит Майк (ML), стоит сохранить этот сценарий на будущее. — JP] Сценарии часто полезны, даже если вы не занимаетесь программированием. Правильно набрать сложную команду непросто. Представьте, что вам необходимо ввести следующую абракадабру: % soelim a.ms b.ms | pic | eqn | tbl | troff -ms -a | more (Это не надуманный пример. Мне приходилось несколько раз набирать именно эту команду.) Вместо того чтобы тратить время на набор строки и редактирование ошибок, с помощью любимого редактора можно создать простой временный сценарий: # сценарий "foo" для однократного использования soelim a.ms b.ms I pic I eqn I tbl I troff -ms -a -rzl I more Работая в редакторе, поэкспериментируйте со сценарием, пока строка не станет правильной. С самым заурядным редактором работать проще, чем с "сырой" командной строкой. Затем выполните следующее: % 3h fOO Если в дальнейшем эта команда использоваться не будет, можете удалить файл сценария или воспользоваться временным файлом (гг.оз). Но прежде чем вводить команду гт, подумайте: возможно, впоследствии придется повторить то, что вы рассчитывали выполнить всего один раз. Дайте сценарию выразительное имя и сохраните его в своем каталоге bin (4.02). Теперь вы программист. :-) Напоследок заметим, что это хороший способ ввода сложных последовательностей команд. - ML 9.16 Подстановка результатов выполнения команды Пара обратных кавычек (' *) обеспечивает подстановку результатов выполнения команды. Это очень удобно: стандартный вывод одной команды можно использовать как аргумент другой команды. Рассмотрим пример. Предположим, необходимо отредактировать все файлы текущего каталога, содержащие слово error. Введем следующее: -1/5.07 . $ vi 'grep -l error *.c" 3 files to edit "bar.c" 254 lines, 28338 characters Почему это работает? Почему фокус удается? Во-первых, задумайтесь над тем, как бы вы это сделали, не прибегая к специальным приемам? Вы бы воспользовались командой grep. Как сэкономить время при вводе командной строки 145
9.16 чтобы выяснить, какие файлы содержат слово error, и программой vi, чтобы отредактировать полученный список. $ grep error *.с bar.с: error ("input too long"); bar.с: error ("input too long"); baz.c: error ("data formatted incorrectly"); foo.c: error ("can't divide by zero"); foo.c: error ("insufficient memory"); * $ vi bar.с baz.c foo.c Можно ли сократить эти команды? Да, путем подстановки результатов выполнения команды. Прежде всего, нужно изменить команду grep так, чтобы она выводила список файлов без текстов. Реализовать это позволит команда grep -/: $ grep -l error *.c bar. с baz.c foo.c При наличии опции -/ имена файлов выводятся только раз, даже если файл содержит много подходящих строк. (Это позволяет думать, что команда grep -l разработана именно для указанных целей.) Теперь эти файлы нужно отредактировать, поэтому заключим команду grep в обратные кавычки и сделаем ее вывод аргументом команды vi: $ vi 'grep -1 error *.c" 3 files to edit "bar.c" 254 lines, 28338 characters $ У вас может возникнуть вопрос относительно различий между именами файлов (каждое имя в отдельной строке), выданными командой grep, и аргументами, набираемыми пользователем в командной строке. В обоих случаях shell работает без проблем. И символ новой строки, и пробел между обратными кавычками являются разделителями аргументов. Не обязательно, чтобы список, используемый при подстановке результатов выполнения команды, содержал имена файлов. Посмотрим, как можно отправить почтовое сообщение (из) всем пользователям, зарегистрированным в системе в настоящее время. Для этого понадобится следующая командная строка: % mail joe lisa franka mondo bozo harpo ... Чтобы получить ее, нужно проанализировать, какие команды UNIX могут дать необходимые выходные данные. Для получения списка зарегистрированных пользователей можно воспользоваться командой who (si.04). Команда who выдает также другую информацию (например, время входа в систему), которую можно отсечь с помощью команды cut (З5.му. % who | cut -cl-8 joe lisa franka lisa joe mondo joe Некоторые пользователи зарегистрировались несколько раз. Получить список имен без повторов позволит команда sort -и (Зб.м). Все удалось. Теперь команду получения имен пользователей можно заключить в обратные кавычки: % mail 'who | cut -cl-B | sort -u' 146 Часть вторая. Поручите грязную работу компьютеру
9.18 Чтобы убедиться, что эта команда выполняется корректно, замените команду mail командой echo (ему. % acho 'who I cut -cl-8 | sort -u' bozo franka harpo joe lisa mondo Поработав некоторое время с UNIX, вы убедитесь, что подстановка результатов выполнения команды является одной из самых полезных возможностей системы. Не раз сложится ситуация, когда команду, генерирующую список аргументов, потребуется заключить в обратные кавычки и использовать как аргумент другой команды. Иногда необходимо вложение (45.31) обратных кавычек. В этом случае становятся полезными операторы $ () в bash и fah (которые заменяют открывающую и закрывающую обратные кавычки соответственно). При подстановке результатов выполнения команд могут возникнуть некоторые проблемы (9.20), но, скорее всего, вы с ними не столкнетесь. Книга содержит множество примеров подстановки результатов выполнения команд. Вот некоторые из них: создание уникальных имен файлов (i6.it), удаление ряда файлов из списка (23.21), подсчет количества слов (29.06), получение списка файлов ps.o9), установка строки приглашения ом, 7.об, 7.П), установка переменных (5.04, 45.зо), работа с метасимволами os.os), выполнение циклов (40.02). - JP 9.17 Обработка длинного списка аргументов Иногда требуется выполнить команду с длинным списком имен файлов в качестве аргументов. Мы продемонстрируем, как создать такой список, не вводя каждое имя вручную. В этом случае список помещается во временный файл (21.озу. % Is > /tmp/mikel % vi /tmp/mikel ...удаление лишних имен файлов... '...'9.16 % команда_обработки_файлов 'cat /tmp/mikel' % rm /tmp/mikel Строка с командой vi включена, чтобы напомнить вам о возможности редактирования списка (например, можно удалить имена файлов, которые не следует обрабатывать). Возможны следующие проблемы: если список файлов имеет большой объем, ваш интерпретатор может оказаться не в состоянии обработать командную строку такой длины. Тогда воспользуйтесь командой xar%s (9.21). Если в вашей системе данная команда отсутствует, прибегните к другим способам (9.23) решения проблемы. Один из них описан в параграфе 9.24. - ML 9.18 Подстановка процессов Используете ли вы временные файлы для чтения некоторыми командами? Предположим, вам нужно сравнить два файла при помощи команды comm os.12). Эта команда требует упорядоченных файлов, а у вас есть неупорядоченные, и вы вынуждены набирать: bash$ sort filel > /tmp/filel.sort bash$ sort file2 > /tmp/file2.sort bash$ comm /tap/filel.sort /tmp/file2.sort Этот процесс можно упростить. Постановка процессов в bash В интерпретаторе bash есть оператор <(имя_процесса), который выполняет указанный процесс и направляет вывод в именованный канал. После этого имя файла канала становится аргументом командной строки. Рассмотрим пример. Как сэкономить время при вводе командной строки 147
9.18 bash$ cat filel rcsdiff.log runsed runsed.new echo.where foo bash$ cat fil«2 newprogram runsed echo.where foo bash$ consn <(eort filel) < (sort fil«2) echo.where foo newprogram rcsdiff.log runsed runsed.new (Команда сотт в первом столбце отображает строки только из файла filel, а во втором столбце — из file2. Третий столбец содержит строки, найденные в обоих файлах.) Рассмотрим более внимательно, как все происходит. При установке опции -х /s.i7) shell обозначает процессы высшего уровня знаком +, а процессы второго уровня — знаками ++: bash$ set -х bash? comm <(sort filel) <(aort f±l«2) + comm /tmp/sh-np-alll67 /tmp/sh-np-blll67 ++ sort filel ++ sort file2 echo where foo newprogram rcsdiff.log runsed runsed.new Сценарий создал именованные каналы в каталоге /tmp. Bash выполнил каждую команду sort, направил выходные данные в именованный канал и поместил имя файла канала в командную строку. Когда выполнение программы сотт завершилось, именованные каналы были удалены. В некоторых случаях при выполнении оператора <(имя_процесса) возникают проблемы: чтение именованного канала процессом "зависает" и данные на выход так и не поступают. Это случилось, например, когда я заменил команду сотт командой diff, от которой не получил никакого вывода. Проблема была решена путем закрытия стандартных выводов каждого процесса при помощи оператора >&- (45.2iy. bash$ diff <(sort filel; exec >«-) <(sort file2; exec >£-) Команда diff сразу же продемонстрировала мне разницу между двумя упорядоченными файлами. В bash есть похожий оператор — > ( ). Он направляет на вход процесса данные из именованного канала. Автоматическое создание временных файлов Если вы не работаете с интерпретатором bash, то можете воспользоваться сценарием с именем / (восклицательный знак),* который выполняет команду, сохраняет результаты во * В интерпретаторе С shell восклицательный знак (без последующего пробела) используется также для работы с перечнем ранее введенных команд (им, n.is>. Этот сценарий не конфликтует с механизмом обработки перечня команд в csli. В интерпретаторе basil восклицательный знак предназначен для замены кода завершения команды противоположным кодом. Но если вы применяете этот интерпретатор, наш сценарий вам не понадобится. 148 Часть вторая. Поручите грязную работу компьютеру
9.18 временном файле, а затем помещает имя временного файла в свой стандартный вывод. При вызове этого сценария следует использовать обратные кавычки (9.16). Вот как можно видоизменить пример из предыдущего параграфа: % comm '! sort filel' '! sort file2' echo where foo newprogram rcsdiff.log runsed runsed.new А почему нельзя использовать следующую строку? % comm 'sort filel' 'sort file2' Потому что программа comm (как и большинство программ UNIX) в качестве аргументов требует имена файлов. При использовании одних обратных кавычек в командную строку был бы помещен список имен (упорядоченное содержимое файлов filel и fuel). Чтобы проследить, что же происходит, можно использовать Bourne shell и установить его опцию -х (8.17). Интерпретатор будет отображать имена выполняемых команд со знаками + перед- каждым из них: $ set'-х $ comm '• sort filel' ' sort file2' + ! sort filel + ! sort file2 + comm /tmp/bang3969 /tmp/bang3971 echo where foo newprogram rcsdiff.log runsed runsed.new Сценарий создает свои временные файлы в каталоге /tmp. Их можно удалять. Если этим сценарием пользуетесь только вы, возможно, вам будет достаточно следующей командной строки: % nri /tmp/bang[1-9]* Если в вашей системе есть несколько пользователей, безопаснее применять команду find (П.му. % find /tmp -name 'bang*1 -user имяг_пользователя -exec rm {) \; При частом использовании сценария команду очистки можно оформить в виде псевдонима (Ю.02) или сценария либо запустить в фоновом режиме (1.26) из файла .logout (j.in, .ш). Приводим текст сценария /.Конечно, имя можно заменить другим. 3#!/bin/sh temp=/tmp/bang$$ case $# in 0) echo "Usage: 'basename $0' command [args]" 1>&2 echo $temp exit 1 "$&■■ 44.15 *) "$@" > $temp echo $temp i t esac - J? Км'Шономить время при вводе командной строки 149
9.19 9.19 Для нетерпеливых: досрочный ввод команд Интерпретаторы shell в UNIX имеют функцию досрочного ввода, позволяющую продолжать ввод, пока компьютер о чем-нибудь "думает". Это удобно, если необходимо по очереди выполнить несколько команд, а вы не любите ждать, пока завершится предыдущая команда, чтобы ввести следующую. Функция досрочного ввода позволяет продолжать ввод, даже если shell "занят" (т.е. при отсутствии приглашения). Ввод может быть продолжен и в том случае, если текущая команда (выполняемая в интерактивном режиме (/.26)) заполняет экран данными (в этом случае есть вероятность возникновения путаницы). Все дополнительные введенные команды будут выполнены, как только завершится работа интерактивной команды. Проше всего сочетать досрочный ввод и команду sleep (40.02), которая организует временную задержку на заданное количество секунд: % sleep 25 lpr article % % Команда sleep "бездействует" в течение 25 секунд. Поэтому, нажав клавишу [RETURN] в конце строки с этой командой, вы не увидите приглашения. Тем не менее, вы можете ввести следующую команду (lpr), которая будет выполнена по завершении команды sleep. Драйвер терминала (42.oi) читает вводимые вами символы, сохраняет их в буфере и передает интерпретатору shell, когда он готов к этому. Что произойдет в случае задержки? Чтобы прервать выполнение фонового задания, достаточно нажать клавишу прерывания (38.09) (например, [CTRL-c]). Тогда UNIX отменит предварительно введенные команды и не станет их выполнять. (То же происходит при нажатии клавиш [CTRL-z] или при получении фоновым заданием любого другого сигнала.*) Обычно это как раз то, что нужно, поскольку при нажатии [CTRL-c] вы, как правило, не желаете выполнять никаких заданий, предварительно поставленных в очередь. Функция предварительного ввода удобна не только для выполнения дополнительных команд. Если для запуска ваших команд требуется много времени, можно набирать их входные данные, пока выполняется предыдущая команда. Приведем пример (вряд ли вам приходилось сталкиваться с подобной ситуацией). В нем используется утилита^ (52.07), предназначенная для подключения к удаленному серверу. % ftp open golgonooza loukidea ftp> Connected to golgonooza. 220 golgonooza FTP server ready. Name (golgonooza:mikel): 331 Password required for loukides. Password: До запуска этой утилиты мне удалось ввести первую команду ftp и свое регистрационное имя (которое команда ftp запрашивает в обязательном порядке). Скорее всего, досрочный ввод пароля не допускается, хотя мне попадались странные системы, которые позволяли делать это. Даже если вам удастся выполнить этот прием, лучше не пользоваться им. Утилита ftp не успеет отключить отображение введенных с клавиатуры символов, пароль появится на терминале и каждый сможет прочесть его. Досрочный ввод требует определенной сноровки. Нужно точно знать, когда и какие входные данные потребуются приложению. Впоследствии вы обнаружите, что в некоторых приложениях (особенно полноэкранных) досрочный ввод невозможен. Однако предсказать, что будет и чего не будет, невозможно. Мне встречались реализации редактора Emacs, которые позволяли редактировать файл еще до его появления на экране. [Редактор W в большинстве случаев позволяет вводить команды в процессе своего запуска.— JP\ * Иногда прерывание интерактивной команды не приводит к выполнению досрочно введенных команд, например команд в окне интерпретатора shell редактора Emacs. 150 Часть вторая. Поручите грязную работу компьютеру
9.20 Мой способ досрочного вода ужасен: я не рекомендую пользоваться им, но в то же время показываю, чего можно достичь с его помощью. Часто я, вместо того чтобы активизировать программу чтения новостей (из), перехожу в каталог новостей и при помощи команды grep (27.0I) ищу интересные заметки. В процессе просмотра страниц, которые демонстрирует мне команда grep, я ввожу команду тоге (25.оз) с номерами заинтересовавших меня заметок. К тому моменту, когда команда grep просмотрит всю группу новостей, я сформирую команду тоге и буду готов к прочтению заметок, о которых мне сообщила команда grep. (Я не утверждаю, что^ такой способ идеален, но на медленных терминалах он удобен.) - ML 9.20 Слишком много файлов в командной строке Пара обратных кавычек (9.16) обеспечивает выполнение такой команды, как find (n.oi), и включение ее выходных данных в командную строку другой команды. Например, благодаря строке рг 43.07 % рг -n 'find . -type f -mtlma -1 -print' | lpr Ipr 43.02 список всех модифицированных сегодня файлов будет передан команде рг, а с выхода этой команды по каналу —'команде lpr (на. принтер). Однажды я выполнял глобальную подстановку во множестве файлов (34.оз, 2S.09) и при попытке напечатать все отредактированные файлы получил сообщение об ошибке: Arguments too long (слишком длинные аргументы). Оказалось, что команда find выдает настолько длинный перечень файлов, что он переполняет командную строку. % рг -п ./путь/файл1 ./путь/файл2 ./путь/фаЯлЗ ... | lpr (Такое может случиться с любой командой, заключенной в обратные кавычки, а не только с командой find.) Мне пришлось делить на части выходные данные команды find, чтобы они не были слишком длинными. Это происходило в системе UNIX, в которой отсутствовала "великая" команда xargs (9.21). Вместо нее я решил использовать команду fiiu (35.02). Эта команда получала данные со стандартного ввода и формировала такие блоки текста, чтобы заполнилась выходная строка. Поскольку я применял команду fmt -1000, длина выходных строк достигала 1000 символов. Достаточно, чтобы не слишком часто выполнять команду рг. Я запустил Bourne shell, который позволяет передавать данные по каналу на вход оператора цикла (45.23). Shell выводит вторичные приглашения (9.щ, пока вы не закончите вводить операторы цикла. % sh $ find . -type f -mtime -1 -print | > fmt -1000 I read 44.13 > while read files > do pr -n $files > done I pr exit 3S.04 $ exit Shell помещает каждую строку имен файлов, поступающую с выхода команды fmt -ЮОь, переменную files, запускает команду рг с этими именами и передает данные по каналу t. выхода команды рг на стандартный ввод команды lpr. А она не знает, что получает данные от команды рг. Единственное, что она видит, — это последовательность 66-строчных страниц. Если в вашей системе есть команда xargs, аналогичную операцию можно выполнить следующим образом: % find . -type f -mtime -1 -print | xargs pr -n | lpr yj/ Команда xargs читает текст со стандартного ввода и, когда фрагмент текста достигнет \<1 достаточного объема, запускает командную строку рг -п путь/файл путь/файл. . . . Затем команда xargs продолжает чтение и снова запускает команду рг до тех пор, пока не прочтет весь текст. Выходные данные команды xargs (которые на самом деле поступают с выхода все той же команды рг) направляются команде lpr. 19Л2] Ям сэкономить время при вводе командной строки 151
И последнее замечание (от ML): нет оправдания для таких команд, как xargs, которые являются ответом на ошибки разработчиков. UNIX должна работать с командными строками любой длины, и, возможно, в какой-нибудь будущей версии так оно и будет. - JP Обработка длинных командных строк с помощью команды xargs Команда xargs является одной из тех утилит UNIX, которые при первом знакомстве кажутся совершенно бесполезными, но потом становятся одним из самых удобных инструментов. Если в вашей системе команда xargs отсутствует, инсталлируйте ее с компакт-диска. Команда xargs читает группу аргументов со своего стандартного ввода, а.затем запускает с ней команду UNIX. Чтение аргументов и запуск команды повторяются^ пока не будут исчерпаны все аргументы. Обратные кавычки выполняют ту же функцию, но в случае их использования все аргументы команды передаются сразу. При этом может появиться сообщение об ошибке: Too many arguments (слишком много аргументов). Вот несколько примеров: • Если нужно распечатать список файлов большого каталога, направьте вывод команды Is в файл. Отредактируйте этот файл, чтобы в нем остались только те имена, которые вам нужны. Направьте файл на стандартный ввод команды xargs: % Is > allfiles.tmp % vi allfiles.trap < /3.01 % xargs lpr < allf iles . trap Как выполняются эти команды? Если файл allfiles.tmp содержит строки, подобные следующим: % cat allfiles.tmp afile application yoyotest zapme команда xargs запускает одну или несколько команд lpr, причем каждую — с группой аргументов, пока не прочтет все слова из этого файла: lpr afile application ... lpr ... yoyotest zapme • Стандартный вывод команды xargs является стандартным выводом тех команд, которые она запускает. Поэтому если создан упоминавшийся выше файл allfiles.tmp и нужно сначала отформатировать данные при помощи команды £г (4з.от), то следовало бы ввести следующее: % xargs pr < allfiles.tmp | lpr В этом случае команда xargs запускает команду pr, a shell по каналу передает выходные данные* команде lpr: pr afile application ... pr ... yoyotest zapme В следующем примере команда find (two получает список всех файлов дерева каталогов. Затем мы используем команду xargs, чтобы прочесть имена файлов, и запускаем команду grep -I (is.07) для поиска файлов, имена которых содержат слово warning. Найденные1 * На самом деле интерпретатор shell передает по каналу выходные данные команде xargs. Как я отмечал, команда xargs пересылает данные со стандартного вывода команд, которые она запускает, на свой стандартный вывод. —— 1 1 1. |„ I, -—-- 1Ь2 Часть вторая. Поручите грязную работу компьютера 9.21 9.21 ш xargs
9.22 [13M1 * имена файлов, мы передаем конвейеру, состоящему из команд рг и Ipr (см. предыдущий пример). % find . -type f -print I xargs grep -1 WARNING | xargs pr | lpr "Что-что?" — спросите вы. Рассмотрим все более детально. Вывод команды find представляет собой список имен файлов, например: ./afile ./bfile . . . ./adir/zfile и т.д. Первая команда xargs передает эти имена одной или нескольким командам grep -I: grep -1 WARNING ./afile ./bfile ... grep -1 WARNING ./adir/zfile ... В стандартном выводе команд grep содержится список имен подходящих файлов. Этот список передается другой команде xargs, которая запускает команды рг с теми командами, которые нашла команда grep. UNIX..— удивительная и прекрасная система! • Не во всех случаях необходимо, чтобы команда xargs запускала свои команды со всеми аргументами, которые могут поместиться в командной строке. Опция -и позволяет задать максимальное количество аргументов, которые команда xargs будет передавать каждой команде. Другая удобная опция, -р, обеспечивает вывод сообщения перед выполнением каждой команды. Допустим, у меня есть каталог, наполненный файлами с ошибками (имена которых заканчиваются символами .bad) и исправленными версиями (fixed). Я использую команду Is для передачи списка файлов команде xargs, которая читает два имени за раз, а затем спрашивает, хочу ли я запустить команду diff-c, чтобы сравнить эти два файла. Вывод запроса и запуск команд diff -с продолжается до тех пор, пока не будут перебраны все пары: % Is chapl.bad chapl.fixed chap2.bad chap2.fixed chap9.bad chap9.fixed % Is | xargs -p -n2 diff -c diff -c chapl.bad chapl.fixed ?...y ...вывод команды diff для chapl... diff -c chap2.bad chap2.fixed ?...n diff-c chapl.bad chap3.fixed ?...y . . . вывод команды di ff для ctiap3. . . Как объясняется в следующем параграфе (9.22), команда xargs может функционировать неправильно, если внутри аргумента содержится пробельный символ. К счастью, GNU-версия команды xargs (см. следующий параграф) позволяет решить подобную проблему. - JP 9.22 Команда xargs: как решить проблемы с символами пробела и новой строки Команда xargs получает данные со стандартного ввода и разделяет аргументы по пробелам и символам новой строки. В UNIX имена файлов могут содержать пробелы и символы новой строки (хотя это встречается очень редко). При использовании таких имен возможны сбои в работе команды xargs. Например, у меня есть каталог, заполненный копиями заметок из Usenet. Имена файлов совпадают с темами заметок; Ям сэкономить врет при вводе командной строки 153
9.23 % Is A use for the "yes" command Beware UNIX Security Holes Causes of 'test' errors Проблемы возникают при запуске таких команд, как следующая: % find . -type f -mtime +7 -print | xargs rm Если команда find выведет путевое имя ./Beware UNIX Security Holes, то команда xargs сообщит команде rm, что нужно удалить четыре файла: ./Beware, UNIX, Security и Holes. Скорее всего, я получу от команды пп четыре сообщения об ошибках, поскольку файлов с такими именами не существует. Если бы они существовали, то были бы удалены, чего как раз не требуется. Символы новой строки могут вызвать такие же проблемы. Некоторые версии команды xargs (см. ниже) лучше справляются с этой задачей. Приведем простой тест, позволяющий увидеть, насколько хорошо работает версия этой команды в вашей системе. Создайте пустой каталог, файл с пробелом в имени и файл с символом новой строки в имени. Попробуйте удалить один из этих файлов: % mkdir temp % cd temp % touch 'Just testing' % touch 'some\ file' % find . -print | xargs rm ./Just: No such file or directory testing: No such file or directory some: No such file or directory file: No such file or directory Эта команда xargs разбивает имена файлов на отдельные слова. В противном случае файлы были бы удалены. GNU-версия команды xargs (есть на компакт-диске) имеет опцию -0 (нуль). Это означает, что путевые имена, которые она читает, разделяются символами NUL, а не пробельными символами. GNU-версия команды find (также есть на компакт-диске) имеет опцию -printO, которая между путевыми именами вместо символа новой строки вставляет символ NUL. Используйте эти команды вместе: % find . -type f -mtime +7 -printO | xargs -0 rm Поскольку путевые имена в UNIX не могут содержать символы NUL, данная комбинация команд всегда работает корректно. (Воспользуйтесь ею!) - JP 9.23 Слишком много аргументов... Что делать? Когда shell заменяет метасимволы ps.ai) именами файлов из большого каталога или длинными путевыми именами (н.о2, is.o6), иногда могут быть нарушены ограничения на длину строки. Тогда появляется сообщение следующего вида: рг 43.07 % рГ */* | 1рГ Arguments too long. В некоторых случаях подобных проблем можно избежать. Фокус в том, чтобы разбить командную строку на части при помощи точек с запятой (s.os) и запустить порожденный интерпретатор shell (н.07), чтобы объединить выходные данные. Перепишем предыдущую командную строку следующим образом: % (pr [a-f]*/*;pr [g-m]*/*;pr [n-z]*/*) | lpr [1.15] /54 Часть вторая. Поручите грязную работу компьюп
9.24 Первая команда выводит имена файлов каталогов, которые начинаются с букв от а до f и т.д. Как определить, в каком месте разделять командную строку? Никакой премудрости тут нет. Сколько частей нужно и каким образом делить, зависит от того, сколько каталогов и файлов нужно обработать, а также от версии UNIX. Определите это опытным путем. Для этой цели подойдет команда true. В предыдущем примере я подбирал способ разделения на части до тех пор, пока система не перестала "обижаться": % true [a-m]*/* Arguments too long. % true [a-f]*/* % true [g-z]*/* Arguments too long. % true [g-m]*/* % true [n-z]*/* Этот прием подходит для таких команд, как рг, у которой выходные данные упорядочены и сохраняют целостность независимо от того, обрабатываются отдельные группы файлов или все файлы. Некоторые команды начинают каждый список с заголовка. Например, команда Is -/перед списком каталога выводит заголовок total число. Команды такого типа не совсем хорошо реализуют описанный прием, потому что на выходе получается смесь из данных и нескольких заголовков вместо одного. И все-таки лучше получить хоть что-то, чем ничего. - JP 9.24 Редактирование списка файлов Такое впечатление, что для поиска необходимых файлов наиболее часто я применяю команду Is с опцией -/, а иногда и с другими опциями. Я пользуюсь также командой grep и ее различными версиями (27.01) для поиска файлов, содержащих определенный текст. Независимо от того, какая команда используется, я переадресовываю вывод (n.oi) во временный файл (21.02, 21.оз) и затем редактирую его. В результате я получаю список имен файлов, который могу использовать в качестве аргумента некоторых других команд или сохранить в переменной shell (бщ. Приведу два примера. Хотя они предполагают применение редактора vi, можно воспользоваться любым другим редактором UNIX, который работает с обычным текстом. Возможно, вам подойдут некоторые утилиты UNIX. Кэш файлы редактировались сегодня? Пока я работал над написанием книги, мой текущий каталог мог содержать до 1000 файлов. Чтобы выяснить, какие файлы подвергались редактированию в течение дня, я применял команду Is -It (16.02). Эта команда выводит список, в начале которого указаны файлы, измененные последними. Редактируя этот список, я исключал все строки, которые не относятся к текущему дню, а затем в каждой строке удалял все, кроме имени файла. Наконец, я использовал обратные кавычки и команду гсц о.зз), чтобы скопировать файлы в каталог ptbackup на компьютере /server. 1. Получение списка файлов и запуск редактора: % is -it > /tmp/bk$$ В 11.03 % vi ! $ vi /tmp/bk28182 2. Удаление всех строк, кроме тех, которые относятся к 29 февраля: total 4294 « удалить -rw-r~r— 1 jerry 1529 Feb 29 17:25 а7630 -rw-r—г— 1 jerry 1864 Feb 29 16:29 a0147 ...Сохранить эти строки... -rw-r—r~ 1 jerry 1772 Feb 29 09:01 al900 to* сэкономить время при вводе командной строки 155
9.24 -rw-r—r-- 1 jerry • 2693 Feb 29 08:51 a0031 -rw-r—r— 1 jerry 744 Feb 28 23:35 д7600 « удалить -rw-r--r— 1 jerry 1957 Feb 28 22:18 a5210 << удалить ... Удалить остальное... 3. Использование в редакторе v/ команды | (вертикальная черта) (зо.з4) для поиска колонки, после которой начинаются имена файлов. Вот куда, например, перемешали курсор команды 30 I, 39| и 45]: -rw-r—r— 1 jerry 1529 Feb 29 17:25 а7630' 30Л 39л 45л 4. Таким образом, в каждой строке мне нужно удалить символы в позициях 1—45. Проще всего использовать фильтр (зо.22) с командой colrm I 45 (35.15) или cut -с46- (35.14>: :%!cut -c46- 5. Теперь файл выглядит следующим образом: а7630 а0147 а1900 а0031 Итак, перед нами — только имена файлов. Полученный список можно отсортировать при помощи команды : %! sort или отредактировать. Сохраняю файл и выхожу из редактора. 6. Передача списка имен файлов команде, которую я хочу выполнить: % rep 'cat /tmp/bk$$' fserver.-ptbackup Применимо к большинству команд UNIX, а не только к гср. Так, чтобы распечатать файлы, можно выполнить следующую команду: % lpr 'cat /tmp/bk$$' 7. Если бы мне пришлось много работать с этими файлами, то вводить команды, заключенные в обратные кавычки, было бы утомительно. Сохраним имена файлов в переменной shell (ему. — Выберите имя для переменной. Обычно я употребляю имя temp, которое нигде больше не использую. Перед установкой переменной ее имя следует проверить при помощи команды echo " $имя_переменной". Убедитесь, что переменная пуста и не используется. — Сохраните имена файлов в переменной: % set temp=('cat /tmp/bk$$~) ...С shell ? tentp="'cat /tmp/bk$$' ■•. ...Bourne shell — Теперь вместо имени файла можно использовать переменную. Например: % ср $temp имя_каталога % vi $temp % (Иногда проще использовать команду .find с опцией -newer (п.щ, однако эта команда ищет файлы и в подкаталогах, если опция -prune (ШЗ) не установлена.-). Не забудьте удалить временный файл по завершении работы с ним. Поиск текста при помощи команды grep Опция -/ (15.07) команды grep обеспечивает вывод списка имен файлов, содержащих заданную строку. Иногда требуется просмотреть строки, найденные командой grep, чтобы решить, нужен данный файл или нет. Вот как это делается: 1. Выполните поиск файлов, содержащих заданную строку. Убедитесь, что ваша команд» grep перед каждой найденной строкой выводит имя файла: % egrep -i "summer(у|1«з)" * > /tmp/bk$$ 1Яв Часть вторая. Поручите грязную работу компьютеру
9.25 2. Отредактируйте временный файл. Он должен содержать примерно следующие строки: a0066:Here is summary of the different components: aOl83:Summary: String functions in awk a0183:for a summary of all the string functions a0184:Let's start with a short summary how awk treats command al000:Here's a summary of the rules that UNIX uses to interpret paths: alOOO:Here's a summary of the different sorts of wildcards available: al680:cumulative summary files and ASCII reports in a2710:In summary, \fIcomm\fP is similar to \fIdiff\fP: Оставьте по одной строке для каждого файла из разряда тех, с которыми вы собираетесь работать. Остальные удалите: a0066:Here is summary of the different components: aOl83:Summary: String functions in awk alOOO:Here's a summary of the different sorts of wildcards available: 3. Удалите все данные после имен файлов. Если имена файлов не содержат двоеточий, при помощи редактора vi можно удалить все двоеточия, а также все следующие за ними данные. Используйте следующую команду: :%з/:.*// Если есть вероятность повторения имен файлов (это может быть связано с тем, что в некотором файле команда grep нашла несколько строк), отфильтруйте имена файлов при помощи команды sort -и, чтобы избавиться от дубликатов. В редакторе w введите следующую команду: :%!sort -u Результирующий список будет следующим: аООбб а0183 alOOO Как и в предыдущем примере, этот список можно использовать в качестве аргумента любой команды. Надеюсь, что благодаря этим двум примерам вы поняли, как нужно действовать: каким образом определить подходящие утилиты UNIX, как собрать и отредактировать их выходные данные, чтобы получить нужный результат. - JP 9.25 Команда repeat в С shell С shell имеет встроенную команду, которая позволяет повторно выполнять другие команды: % repeat л команда Все, что от вас требуется, — задать количество повторов и имя команды, которую нужно выполнить несколько раз. Приведем элементарный пример: % repeat 4 echo Enter name: Enter name: Enter name: Enter name: Enter name: Очень просто, не так ли? Только представьте себе, что Джек Николсон смог бы сделать в фильме "Сияние", если бы заменил свою печатную машинку UNIX-системой: % repeat 500 echo "All work and no play makes Jack a dull boy." Как сэкономить время при вводе командной строки 157
9.26 tcl, ft Ладно, это шутка. Эта команда очень полезна, и я докажу это. Вы можете применять ее в следующих целях: a) печать трех копий файла memo: % repeat 3 pr memo | lp b) выполнение команды popd (ы.об) четыре раза подряд для очистки стека каталогов: % repeat 4 popd c) добавление пятидесяти новых шаблонов в файл report: % repeat 50 cat template » report В некоторых версиях С shell при работе с кавычками возникают ошибки (см. параграф 47.02). - DG 9.26 Программа Expect Программа Expect предназначена для управления такими интерактивными программами, как telnet (из) и passwd. Эти и многие другие приложения выводят приглашение и ожидают от пользователя ввода ответной строки. Такое интерактивное взаимодействие можно автомати- expect зировать, написав простые Expect-сценарии. В результате программа Expect сможет запускать интерактивные программы в неинтерактивном режиме. Эта программа позволяет также автоматизировать части диалога, поскольку управление может передаваться от сценария клавиатуре и наоборот. В этом случае задачи можно распределить: сценарию — рутинные задачи, а пользователю — интересные. Как правило, Expect-сценарии пишутся на языке Tcl, хотя допускается использование любого ® ] другого языка. Tcl — это интерпретируемый язык, применяемый во многих приложениях. Язык Tcl является типичным языком семейства shell. В нем есть команды установки переменных (set) и построения управляющих конструкций (if, while, foreach и т.п.), он выполняет математические операции и обработку строк. И конечно, с его помощью можно выполнять команды UNIX. Программа Expect создана на базе Tcl и содержит дополнительные команды для взаимодействия с другими программами. Она названа в честь своей главной команды, expect, ожидающей вывода другой программы. В команде expect задается список ожидаемых строк. После каждой ожидаемой строки указывается действие. Если строка получена, действие выполняется. Следующий фрагмент взят из сценария, автоматизирующего вход в систему. В процессе выполнения этот сценарий ожидает вывода строки welcome, failed или busy, а затем производит одно из соответствующих действий. Запись действия, связанного со строкой busy, демонстрирует, как выполнить несколько команд. Ключевое слово timeout обозначает специальную строку, которая выполняется, если в течение определенного времени не поступила ни одна из заданных строк. expect ( "welcome" break "failed" abort timeout abort "busy" ( puts "I'll wait - the system is busy!" continue } } Автоматизация набора номера Просто удивительно, насколько полезным может оказаться небольшой сценарий. Ниже приведен сценарий, который набирает номер телефона. Он используется для перераспределения обязанностей, поручая компьютеру набирать номера дальней связи, и вызывается с аргументом в виде телефонного номера. 158 Часть вторая. Поручите грязную работу компьютера ,
9.26 spawn tip modem expect "connected" send "ATD$argv\r" # пауза, предоставляемая модему для соединения set timeout 60 expect "CONNECT" В первой строке запускается программа tip, чтобы вывод модема мог быть прочитан командой expect, а на его ввод можно было направить данные с помощью команды send. Как только программа tip сообщит, что модем подсоединен (connected), ему будет дана команда набрать номер ATD с последующим номером телефона. Номер телефона читается из переменной argv — специальной переменной, содержащей аргумент, с которым был вызван сценарий. Четвертая строка содержит комментарий. В нем сообщается, что переменная, устанавливаемая в следующей строке, задает время, в течение которого команда expect будет ожидать соединения и спустя которое она завершится. В этом месте сценарий ожидает, когда будет установлено соединение. Если соединение установлено успешно, система обнаруживает, что пользователь подключен, и выводит приглашение login:. Реальные сценарии, конечно, выполняют больше проверок ошибочных ситуаций. Например, такой сценарий может повторять вызов, если установить соединение не удалось. Главное, вам не придется писать длинные коды, чтобы создать полезный сценарий. Этот сценарий из шести строк заменяет 60 Кбайт исполняемого кода (написанного на С) и выполняет те же функции! Автоматизация вводе пароля Я уже упоминал программы, выполнение которых невозможно автоматизировать средствами shell. Иногда трудно представить, зачем встраивать программу в сценарий. Конечно, авторы таких программ не предполагали, что в этом возникнет необходимость. В качестве примера рассмотрим команду passwd. Команда passwd служит для изменения пароля. Эта команда не позволяет указать пароль непосредственно в командной строке. Наоборот, она интерактивно запрашивает его, причем дважды. Вот как выглядит процедура смены пароля, когда она выполняется системным администратором. (Когда команду запускает пользователь, диалог получается более сложным, поскольку он должен ввести и свой старый пароль.) # passwd libes Changing password for libes on thunder. New password: Retype new password: Если пароль один, проблем не возникнет. Но представьте, что для вас организованы учетные записи на различных компьютерах и вы хотите, чтобы все они содержали одинаковые пароли. Или, скажем, вы являетесь администратором, которому в начале каждого семестра приходится создавать до 1000 учетных записей. Автоматизация запуска команды passwd сразу становится актуальной задачей. Приводим Expect-сценарий, который автоматизирует запуск команды passwd таким образом, что она может быть вызвана из сценария shell. spawn passwd [lindex $argv 0] set password [lindex $argv 1] expect "password:" send "$password\r" expect "password:" send "$password\r" expect eof В первой строке осуществляется запуск программы passwd с именем пользователя, которое передается в качестве аргумента. В следующей строке для удобства пароль сохраняется в переменной. Как и в сценариях shell, предварительно объявлять переменные не нужно. В третьей строке команда expect ожидает строку password:. Когда она будет обнаружена, выполнение сценария продолжится. Как только появится приглашение (password:), пароль в следующей строке будет передан текущему процессу. Символы \г означают возврат каретки. (Синтаксис команд программы fa* сэкономить время при вводе командной строки 159
Expect соответствует стандартным соглашениям языка С относительно работы со строками.) Сценарий содержит две последовательности expect-send, поскольку команда passwd дважды запрашивает ввод пароля для проверки правильности его написания. Последняя команда — expect eof — указывает сценарию ждать символа конца файла на выходе команды passwd. Как и timeout, eof является ключевым словом. Эта команда, по сути, ожидает, когда завершится выполнение команды passwd и управление будет передано сценарию. Вернемся немного назад. Предположим, эта проблема имеет иное решение. Вам посчастливилось получить исходный текст программы passwd. Вы редактируете его таким образом, что при наличии установленного дополнительного флага программа читает свои аргументы из командной строки так же, как это делает Expect-сценарий. Конечно, если исходный текст получить не удалось и вы пишете программу passwd с нуля, нужно позаботиться о засекречивании паролей, об ограничении доступа к базе данных, содержащей пароли, и т.д. Но даже если изменяется исходный текст, задача окажется чрезвычайно сложной. На самом деле программа passwd использует некоторые весьма хитрые приемы. И если вы запустите собственную версию, то молитесь, чтобы ничего не произошло, когда в системе будут произведены изменения. Если поставщик системы добавит сетевую информационную службу (NIS), систему безопасности Kerberos, теневые пароли, различные функции кодирования, вам придется пересмотреть свою программу. Программа Expect поставляется с целым рядом сценариев, в которых показано, как делать многие вещи, невозможные в традиционных интерпретаторах shell. Например, сценарий passmass позволяет за один раз обновить пароль на нескольких несвязанных машинах. Сценарий /ftp добавляет к стандартному клиенту ftp новые команды, позволяющие устанавливать ^-соединения в любом направлении. Сценарий cryptdir зашифровывает все файлы каталога. Есть также симпатичный сценарий, позволяющий двум процессам chess играть в шахматы друг с другом. В программе Expect не существует ограничений на количество интерактивных программ, которыми она может управлять одновременно. [Однако система UNIX может наложить соответствующие ограничения, задав максимальное количество процессов или объем других доступных системных ресурсов. — JP] Как мы тестировали модемы Многие пользователи используют программу Expect для тестирования. Интерактивные программы так же просто тестировать, как и автоматизировать их выполнение. Аппаратное обеспечение также можно тестировать при помощи программы Expect. Например, нам приходилось сталкиваться с проблемами при работе с ненадежными модемами. Мы получали десятки звонков относительно того, что "модем завис", причем без указания типа модема. Мы не могли просить пользователя каким-то образом исследовать проблему, поскольку связь обрывалась. Решить проблему помог Expect-сценарий, который каждый час подключался к каждому модему и исследовал их. Все обнаруженные проблемы фиксировались, так что в итоге вырисовалась довольно наглядная картина о состоянии каждого модема. Как только попадался модем с дефектом или "подвисший" модем, Expect-сценарий посылал системному администратору сообщение электронной почты. После внедрения этого сценария жалобы пользователей на модемы прекратились. Другие проблемы Это только некоторые проблемы, которые позволяет решать программа Expect. Важно, что такие решения не требуют повторной компиляции исходных программ. Вам даже не понадобится исходный текст! Программа Expect справится и с другими задачами. Например, она может дополнить интерактивные программы визуальными средствами типа Motif для управления приложениями при помощи кнопок, полос прокрутки и других графических элементов. Кроме того, Expect-сценарий прекрасно работают в качестве CGI-сценариев и могут запускаться процессами сгоп (4о.п) и inetd [демон, управляющий сервисами Internet. — JP\. Наконец, программу Expect изучить проще, чем вы думаете. Она может отслеживать вводимые вами команды, а затем создавать Expect-сценарий, что сделает еще более простым процесс автоматизации выполнения интерактивных программ. Хорошим источником информации о программе Expect является книга Дона Лайбса (Don Libes) Exploring Expect (издательство O'Reilly & Associates). - DL Часть вторая. Поручите грязную работу компьютеру
10 Псевдонимы команд 10.01 Создание пользовательских команд • Во многих интерпретаторах shell псевдонимы (10.02, юм) (пользовательские команды) представляют собой сокращения длинных командных строк или цепочек команд. • Все интерпретаторы shell, кроме самых старых версий Bourne shell, поддерживают функции (Ю.09). Они являются хорошим средством сокращения командных строк и выполнения последовательностей команд. • В более ранних версиях Bourne shell можно имитировать функции (w.ioj. "Фальшивые" функции ничем не лучше псевдонимов, но все же лучше, чем ничего! - JP 10.02 Псевдонимы для стандартных команд С shell, bash и ksh поддерживают механизм псевдонимов (alias), позволяющий определить сокращенные имена часто используемых команд. Псевдонимы могут соответствовать довольно сложным командам. В этой главе мы ограничимся сведениями вводного характера. В настоящем параграфе мы рассмотрим синтаксис определения псевдонимов в С shell, а в параграфе 10.04 — в bash и ksh. Псевдонимы простых команд Простейший способ применения псевдонима — создание нового имени для обычной команды. Можно, например, переименовать команду Is в dir, если вы привыкли к именам, принятым в системах DOS или VMS: alias dir Is Теперь команда dir эквивалентна команде Is. Вот какие псевдонимы используются чаще всего: alias la Is -a fr включает в список "скрытые" файлы alias If Is -F fr отмечает каталоги символом / alias lr Is -R # выводит список рекурсивно - показывает #'содержимое каталога alias ri rm -i # запрашивает подтверждение на удаление alias mi mv -i # запрашивает подтверждение на замену файла В файле .cshrc знак # является обозначением комментария. Комментарий позволит вспомнить, для чего предназначены пользовательские команды. Это особенно полезно при создании псевдонимов сложных команд (см. следующий параграф). Щвцонщы команд 6 9-171 161
10.02 Псевдонимы сложных команд Вот несколько полезных псевдонимов, которые вы можете использовать в соответствии со своими потребностями: alias emacs /horae/src/emacs/bin/етасз alias clean "rm *~ .*- core *.bak" alias vtext 'setenv EXINIT "source ?HOME/.exrc.text" ; vi' alias vprog 'setenv EXINIT "source ?HOME/.exrc.prog" ; vi' Рассмотрим эти определения более внимательно. Псевдоним emacs ничем не примечателен. Он позволяет вводить длинное имя команды, не добавляя имя каталога в переменную PATH (8.07). (Длинные последовательности поиска не только выглядят непривлекательно, но и замедляют работу системы, хотя в С shell для ускорения поиска используется хэш-таблица (53.01). С другой стороны, shell тратит время и на чтение из файла .cshrc псевдонима, подобного emacs. Определение множества псевдонимов вместо изменения последовательности поиска может замедлить вход в систему и запуск порожденных процессов (З&.М). Если у вас быстродействующий компьютер, то не имеет значения, что использовать — множество псевдонимов или длинную последовательность поиска.) Команда clean очень полезна. Она удаляет резервные копии файлов редактора GNU Emacs, файлы дампа (S3.oi> (которые я обычно не сохраняю) и другие временные файлы. Вместо того чтобы пользоваться какой-нибудь сложной системой очистки диска, запускаемой из процесса сюп, я иногда ввожу в текущем каталоге команду clean. Каждый должен иметь подобную пользовательскую команду и настроить ее так, чтобы избавиться от как можно большего количества "хлама". (Многие пользователи не спешат удалять резервные файлы редактора. Принимайте решение сами.) Третья и четвертая команды несколько сложнее. Вы вводите команду vtext afile, a shell разделяет команды по точкам с запятой и выполняет их по очереди: $НОМЕ 14.11 setenv EXINIT "source $HOME/. exrc. text" vi afile Первая команда устанавливает переменную среды (€.oi) EXINIT. Это заставляет редактор vi читать файл конфигурации (4.т> с именем .exrc.text, находящийся в начальном каталоге. Вторая команда запускает редактор W с аргументами, которые вы ввели. Вы не ограничены одним именем файла. Аргументов может быть сколько угодно: несколько имен файлов, опции редактора vi и т.д. Все они будут фигурировать при вызове редактора. Существует более "элегантный" способ передачи аргументов командной строки пользовательской команде (ш.оз), но он применим, если аргументы находятся в конце определения псевдонима. Обратите внимание на то, что определение псевдонима заключено в кавычки. Почему? Потому что определение представляет собой составную команду {setenv и vi). Мы хотим, чтобы пользовательская команда включала обе составляющие. Подумайте, что бы произошло, если бы мы не заключили в кавычки строку определения псевдонима. alias vtext setenv EXINIT "source $HOME/.exrc.text" ; vi Неправильно! Shell, увидев точку с запятой (S.os) (разделитель команд) вне кавычек, разделит командную строку на две команды. Первая команда определяет псевдоним vtext, с помощью которого запускается только команда setenv, но не vi. После определения псевдонима интерпретатор shell запустит вторую команду — vi — без имени файла. В обоих случаях это не то, что нам нужно. Правильно сработает псевдоним vtext с кавычками вокруг его определения. Внешние кавычки заставляют shell поместить все, что находится между ними, в определение псевдонима. При каждом вызове пользовательской команды точка с запятой в определении псевдонима интерпретируется как разделитель команд. Теперь рассмотрим псевдоним с именем clean. Как и в случае с vtext, определение этого псевдонима нужно брать в кавычки, однако по несколько иной причине. Кавычки предотвращают немедленную замену метасимвола * интерпретатором shell. Так, если ввести: % alias clean rm *.- 162 Часть вторая. Поручите грязную работу компьютера
ЮМ shell сразу произведет подстановку. Если бы текущий каталог содержал файл foo~, команда clean выглядела бы следующим образом: rm f oo~. Это не то, к чему мы стремились. Команда clean, удаляющая один конкретный файл, никому не нужна. Итак, нам нужно изобрести способ предотвращения немедленной (при определении псевдонима) интерпретации символа *. Этот символ должен интерпретироваться позже, при вызове соответствующей команды. Многие параграфы этой книги посвящены отмене специального назначения символов (8.ы, 8.15), но простейший способ заключается в употреблении кавычек. Автоматическое опредепеиие псевдонимов команд и их отмена Псевдоним любой команды можно определить в файле .cshrc. В этом случае он доступен при каждом запуске С shell. (Обратите внимание, что определения псевдонимов не передаются в порожденные процессы (2М), поэтому сохранение определений в файле .login нецелесообразно.) Некоторые любят использовать псевдонимы, заменяющие команды UNIX. Например, можно создать псевдоним rm, запускающий команду mv, который отправляет файл в "корзину", а не удаляет его окончательно.* Переопределение команд может привести к путанице и является опасным (Ю.06). Но в ряде случаев оно приносит пользу. Чтобы временно использовать стандартную команду rm, а не одноименный псевдоним, наберите перед именем команды обратную косую черту (\) (s.ny. % \пп имя_файла Если же вы хотите использовать стандартную команду rm до конца текущего сеанса работы, введите следующую строку: % unalias rm Определение псевдонима будет восстанавливаться при следующем входе в систему (или при следующем запуске нового С shell), пока вы не удалите его из файла .cshrc. Наконец, еще одна деталь. В руководстве по С shell говорится, что определения псевдонимов могут быть вложенными, т.е. включать другие псевдонимы. Нам кажется, что эта возможность скорее вызывает сложности, чем предоставляет какие-либо преимущества. Поэтому мы не можем рекомендовать этот прием. Но попробовать применить его на практике можно. - ML, JP, DG 10.03 Псевдонимы С shell с аргументами в командной строке Часто возникает необходимость в псевдонимах, которым можно передавать аргументы. В качестве примера рассмотрим псевдоним phone: alias phone 'cat -/phonelist | grep -i' Определив такой псевдоним, можно набрать команду phone smith. Shell найдет определение псевдонима phone и выполнит его с аргументом smith (10.02; следующим образом: cat -/phonelist | grep -i smith Использовать таким способом команду cat и канал не эффективно (i3.02). Целесообразно создать команду, работающую следующим образом: grep -i имя -/phonelist Как это сделать? Чтобы создать ссылку на последнее слово предыдущей командной строки, можно использовать оператор !$ для обработки перечня ранее введенных команд (1Ш) в С shell. Оператор ! * относитыКкЪ всем аргументам предыдущей команды. С расчетом на то, что мы будем вызывать га^одной команде за раз, использовать оператор ! $ и определить наш псевдоним можно следующим образом: alias phone grep -i /l $ -/phonelist При выполнении команды phone в текущую команду будет подставлен ее последний аргумент. Если ввести phone bill/интерпретатор shell выполнит команду grep -i bill -/phonelist. В параграфе 23.09 описана программа delete, выполняющая ту же операцию, но лучше. Псевдонимы команд б* 163
ЮМ В приведенном примере мы обратились к другим средствам защиты специальных символов от интерпретатора shell. Необходимо было поставить обратную косую черту перед знаком восклицания, чтобы предотвратить замену символов ! $ последним аргументом предыдущей команды. Нам не нужно, чтобы shell раскрывал символы ! $ при определении псевдонима. Это было бы бессмыслицей. Мы хотим, чтобы интерпретатор вставил предыдущий аргумент во время выполнения команды (в данном случае предыдущим является аргумент пользовательской команды). Но почему мы не можем использовать одинарные или двойные кавычки (s.uf> Ни одинарные, ни двойные кавычки не защищают восклицательный знак от подстановки. А обратная косая черта защищает (8.15). Если хотите убедиться, поэкспериментируйте с такими командными строками: % echo '!!' Выводит имя последней команды % echo ' \ ! ! ' Выводит " .'.' " Первая команда echo показывает, что shell выполняет подстановку из перечня (т.е. заменяет символы ! ! предыдущей командой), несмотря на одинарные кавычки. Вторая строка свидетельствует о том, что обратная косая черта может предотвратить интерпретацию символа ! как специального. Рассмотрим еще одну пользовательскую команду. Мы хотим передать выходные данные команды Is -1 команде more (25.03). В данном случае нужно передать все аргументы командной строки, а не просто последний аргумент. Вот псевдоним, который необходим для решения поставленной задачи: alias lm 'Is -1 \!* I more1 На. этот раз мы используем обратную косую черту для предотвращения немедленной интерпретации восклицательного. знака. Одинарные кавычки защищают символ канала и звездочку. Если оставить только обратную косую черту, вот что получится: % alias lm Is -l \\* \\ more alias: No match. Поскольку обратная косая черта на время отменяет специальное назначение восклицательного знака, shell пытается найти имена файлов, соответствующие метасимволу (hit) !*. Это не удается (за исключением неправдоподобного случая, когда в текущем каталоге есть файл, имя которого начинается с восклицательного знака). Примечание: Ознакомьтесь с правилом приме нения кавычек в определениях псевдонимов. Если команда не выполняет каких-то специфических действий и вы не имеете точного представления " о том, интерпретацию каких специальных символов нужно отменить, заключите определение в одинарные кавычки (') и поставьте обратную косую черту перед каждым восклицательным знаком (\!). .. . Наконец, чтобы выбрать один аргумент из командной строки, используйте оператор \!:л, где л — номер аргумента. Приведем определение последнего псевдонима. В нем используется команда cat (25.02), чтобы сначала добавить файл заголовка к файлу, указанному в первом аргументе, а затем записать результат в файл, указанный во втором аргументе. ~ 14.11 alias addhead 'cat ~/txt/header \!:1 > \!:2' Эта команда имеет два аргумента: файл, к которому добавляется заголовок, и выходной файл. Если ввести строку % addhead foo bar С shell подставит имя файла foo вместо \! :1 и bar вместо \! :2, после чего выполнит такую команду: cat ~/txt/hea'der foo > bar - ML, JP 164 Часть вторая. Поручите грязную работу компьютер]/ ,
1Q.05 10.04 Псевцонимы в ksh и bash Почти все, что мы говорили о псевдонимах, применимо к интерпретаторам Кот shell (ksh) и bash. Одно из отличий связано с синтаксисом команды alias, который имеет следующий вид: $ alias имя=определение Таким образом, между именем псевдонима и определением нужно поставить знак равенства без пробелов. Рекомендуется также заключить определение в одинарные кавычки, если только команда не является специфической и вы хорошо разбираетесь в том, как использовать кавычки (8.Ы) в псевдонимах. Кроме того, нельзя включить аргументы в определение псевдонима, как это делается в С shell с помощью оператора \! рв.щ. Для этой цели предназначены функции (le.oy. В Кот shell псевдонимы перегружены рядом других функций, например, слежением за местоположением исполняемых модулей. Однако это не должно мешать вам создавать такие псевдонимы, которые вам необходимы. - ML 10.05 Сценарии, выполняемые команцой source Псевдонимы — мощное средство csh. Другим таким средством являются сценарии. Для каждого средства характерны свои достоинства. Псевдоним удобен для замены часто используемой последовательности команд, вызова стандартных команд под другими именами и т.д. Сценарии отличаются более гибкими возможностями обработки, в том числе пакетной. У обоих средств есть ограничения, и мы расскажем, как обойти их. Ограничение, связанное с псевдонимами, состоит в том, что они хорошо работают только с одной командной строкой. Рассмотрим пример: alias pp 'set o2=$cwd; popd; set old=$o2; dir_number; record_dir pp; \\ prompt_set; set cd_attempt=(\!*); if (5#cd_attempt > 0) cd $cd_attempt' Я успешно использую эту команду в течение нескольких лет. Тем не менее, я начинаю думать, что сценарий лучше бы подошел для выполнения данной задачи. Затем я задумываюсь об ограничениях сценариев... Сценарии великолепно подходят для решения таких задач, как замена файла, запуск программы и т.п. При использовании сценариев ограничения связаны с тем, что любые изменения, которые они произвели с переменными shell и среды, невидимы (38.оз) для родительского процесса, запустившего данный сценарий. Другими словами, можно написать прекрасный сценарий, который выполнит за вас переход в нужный каталог, если вы в течение пяти секунд не касались клавиатуры. Но когда сценарий завершится, вы окажетесь в том же месте, где находились ранее. Оптимальным решением является комбинирование обоих средств. Рассмотрим следующую строку: 047.05 alias pp ' set. cd_attempt= (\ ! *) ,- source ~/bin/pp_csh' Мы установили переменную и задали исходный файл сценария. Идея состоит в следующем: аргументы командной строки помещаются в переменную, а затем с помощью команды source (44.23) запускается сценарий, выполняющий определенную задачу. В этом случае порожденный процесс (за.04) для выполнения сценария не запускается, и ограничения, присущие сценариям, отменяются. Все это напоминает функции Bourne shell (lo.oy. Вот некоторые советы: • Имена. Сценариям, вызываемым при выполнении пользовательских команд, я люблю давать имена, заканчивающиеся на _csh или .csh. Все сценарии я помещаю в каталог ~/Ып (4.02). [Вместо того чтобы давать сценариям имена, заканчивающиеся на _csh, я помещаю свои сценарии в каталог ~/.lib/csft. — JP\ Псмдонимы команд 165
10.06 • Предупреждение. Сценарий должен выполняться с помощью команды source. Чтобы обратить внимание пользователя на эту особенность, используйте в качестве первой такую строку: «И 45:95 #! /bin/echo sorry, try: source • Инструкции по использованию. Проверьте переменную, которая должна определяться в псевдониме. Если ее нет, можно вывести инструкцию по использованию,и выполнить команду перехода (goto) к концу сценария. i* 47.04 if ($#lg_args == 0) then « 8.18 cat « +++ usage: lg [-a][-p] pattern [command] -a lists all (.dot files) -p pipe resulting list into command +++ goto lg_end endif lg_end: • Опции в пользовательских командах. Работа с псевдонимами в этом случае не сопряжена ни с какими ограничениями, поскольку подключается сценарий. Это обеспечивает определенную гибкость. Вот как можно работать с опциями: unset ls_arg while (! $?ls_arg) switch 47.06 swltch C$lg_args[l]") W 47.05 case "-a" set 6.08 set Is arg="-a" ЛЛ47М shift Ig_args case M-p" set use_pipe shift lg_args default: set ls_arg breaksw endsw end Поэкспериментируйте! Возможно, вы обратитесь к старым определениям псевдонимов и перепишете их как сценарии, выполняемые командой source, которые проще сопровождать. - DS 10.06 Предотвращение рекурсивного вызова псевдонимов в С shell [Информация для bash приведена в параграфе 8.11. — JP] Вот какая ситуация была описана недавно в Сети. Кому-то понадобился псевдоним команды exit (38Щ, запускающий файл "/.exit (u.u) перед выходом из интерпретатора shell. Может показаться, что очевидным является следующее решение: alias exit "source -/.exit; exit" Однако это не работает. При выполнении пользовательской команды exit интерпретатор С shell считает, что она собирается вызвать саму себя. Рекурсия в псевдонимах недопустима во многих интерпретаторах shell, поэтому С shell выводит сообщение об ошибке — Alias loop (циклический вызов псевдонима) — и отказывается выполнять команду. Разорвать цикл можно многими способами. Мне кажется, что лучшим является следующий: alias exit 'source -/.exit; '"'exit' 166 Часть вторая. Поручит» грязную работу компьютеру
10.07 Подробно этот способ описан в параграфе 8.12. Здесь мы лишь отметим, что, если в С shell нужно использовать имя команды внутри определения псевдонима, можно обратиться к следующим приемам: " "имя параметр имя является именем встроенной команды (i.io) или любой внешней команды \имя параметр имя является именем любой внешней команды Как бы это ни было заманчиво (а я должен признать, что если бы это не было заманчивым, то и параграф не был бы написан), я не рекомендовал бы подменять команды псевдонимами. Стандартные команды лучше не трогать. Автор упомянутой заметки избежал бы проблем, если бы назвал свою команду quit, а не exit. Если переопределить команды при помощи псевдонимов, возможны казусы при попытке их использования в учетных записях, в которых ваши псевдонимы не определены. В первую очередь это касается команд, которые выполняют такие непоправимые действия, как перезапись или удаление файлов. Позволю себе привести еще один пример того, с какими проблемами можно столкнуться. Допустим, вы заменили команду exit псевдонимом, выполняющим программу .exit при помощи команды source. Пока все нормально. Но вот вы работаете не в том shell, с которым вошли в систему. Вы установили переменную ignoreegf (ь.ощ, и по непонятным причинам ваш файл .exit исчез (возможно, он находится в испорченном секторе и система не может прочесть его). Теперь вы в ловушке. Если ввести команду exit, выполнение команды source, как и "настоящей" команды exit, станет невозможным. Выход из shell заблокирован. Пользователь, который понимает, что сделал, может набрать unalias exit и восстановить стандартную команду или же ввести ""exit. Новичок вряд ли обладает подобными знаниями, и если вы допустили его к этому псевдониму, то ни с того ни с сего он "застрянет" в интерпретаторе shell, выйти из которого не получается. Возможности UNIX можно расширять до бесконечности. Это одно из главных достоинств этой системы. Однако в таком случае необходимо контролировать, чтобы новые возможности не отменяли стандартные возможности системы. Так, при создании псевдонимов стандартных команд старайтесь давать им уникальные имена. - ML 10.07 Управляющие конструкции в псевдонимах С shell Ошибки в С shell (47.02) не позволяют использовать в определениях псевдонимов управляющую конструкцию if (47.03) с оператором else. По крайней мере, так думал я, пока в декабре 1987 года не встретил заметку Ллойда Зусмана (Lloyd Zusman) в группе новостей comp.unix.questions. Эта заметка (но без имени автора) хранится там и поныне. Фокус заключается в том, чтобы использовать достаточное количество символов обратной косой черты и команду eval (S.io). В качестве примера приводим псевдоним с именем С для компилирования (52.щ программ на языке С. Ему нужно имя исполняемого файла (например, prog), а не исходного (prog.с). Если ввести имя файла с расширением .с, эта пользовательская команда выдаст предупреждение и завершится. Иначе она выполнит следующее: • Переименует все старые файлы имя_программы в имя_программы.оШ. • Выведет сообщение имя_программы SENT то ее. • Откомпилирует файл имя^программы.с. • Если существует файл имя_программы (т.е. компиляция прошла успешно), активизирует команду chmod 311 имя_программы, чтобы защитить файл от случайного чтения такими командами, как cat * и more *. Определение вашего псевдонима может не быть таким сложным. Мы хотели продемонстрировать некоторые приемы (например, вставку конструкции ;/ в другую конструкцию if), которыми можно воспользоваться. Выражения типа =~ и -е описаны в параграфе 47.04. Следите за кавычками. Помните, что shell удаляет один уровень "защиты", когда определяется псевдоним (ю.оз), и еще один — при выполнении команды eval. Следуйте этому примеру, и все будет хорошо. Псевдонимы команд 167
10.08 csh init # Компиляция и.изменение режима доступа к программе на С. # Не используйте расширение имени файла .с. alias С 'eval "if (\!* =- *.с) then \\ echo "С quitting: no .с on end of \!* please/" \\ else \\ if (-e \!*) mv \!* \!*.old \\ echo \!*.c SENT TO cc \\ ее -s \ ! * .с -о \ ! * \\ • if (-е \!*) chmod 311 \!* \\ endif" - JP 10.08 Автоматическая расстановка кавычек в псевдонимах Правильно расставить кавычки и обратную косую черту в определении псевдонима довольно трудно. Дэн Бернстайн (Dan Bernstein) написал две команды, makealias и quote, которые помогают следить за этим. Далее я использовал псевдоним makealias, чтобы не заниматься защитой символов ! и *: % makealias mycat cat 'Is | sed 'l,/!*/d'' | less CTRL-d alias mycat 'cat 4s | sed ' \ " 1,/\ ! Vd'\ " * I less' Я ввел команду makealias mycat, а также строку, начинающуюся с команды cat, и получил определение псевдонима со всеми необходимыми символами кавычек и обратной косой черты. Корректное определение псевдонима направляется на стандартный вывод. Полученную строку нужно использовать при определении псевдонима.* А вот и сами команды quote и makealias: alias quote "/bin/sed -e ' s/\\ ! Л\\\\ !/g' \\ -e ,s/'\\\"/,\\\'\\\\\\\'\\\"/g' \\ -e 's/V'V'/' -e 's/"\S"/'\"/"' est,' ;nif ' alias makealias "quote I /bin/sed 's/Valias \!:1 /' \!:2*" Это довольно громоздко, но работает. — JIK, из телеконференции comp.unix.questions в Usenet, 17 февраля 1991 г. <•) 10.09 Функции ВС shell поддерживаются псевдонимы (ю.ю). А вот до появления System V Release 2 (0)1 интерпретатор Bourne shell почти не имел средств (w.io), позволяющих пользователю создавать собственные встроенные команды. Хотя функции и похожи на псевдонимы, они обладают shjnit большими возможностями. Например, функции способны возвращать код завершения (Ш7) и имеют более понятный синтаксис (io.07>. В bash и Кот shell также есть функции. Чтобы узнать все о функциях shell, прочитайте какую-нибудь книгу о программировании в shell. Примеры вы найдете в файле shjnit на компакт-диске. Приведем примеры псевдонимов из -параграфов 10.02 и 10.03, переделанных в функции для Bourne shell. • Функция la включает в список "скрытые" файлы. Функция If особым образом помечает в списке имена каталогов, исполняемых файлов и т.д.: 1а () { Is -а "$@"; } If () { Is -F "$@"; } [Команда mycat запускает команду cat для всех файлов, имена которых перечислены по алфавиту после введенного вами аргумента. Вывод команды cat передается команде постраничного вывода fe» (2S.04). Допустим, ваш каталог . содержит-файлы afile, count, Jim и report. Тогда при вводе команды mycat count будут выведены имена файлов Jim и report. — JP\ 168 Часть вторая. Поручите грязную работу компьютера
10.10 И пробелы, и точка с запятой имеют значение!* При вводе символы $Q_ (44.1s) заменяются аргументами командной строки (другие опции, каталоги или имена файлов), если последние заданы: $ 1а -1 каталог ...выполняется команда Is -а -1 каталог • Следующая функция — сиг — выводит имя текущего каталога и его содержимое: cur О { pwd Is } Последний пример демонстрирует, как нужно писать функцию, включающую несколько строк. При использовании такого стиля — с закрывающими фигурными скобками в отдельной строке — точка с запятой после каждой команды не нужна. - JP 10.10 Имитация функций и псевдонимов в Bourne spell Если вы используете интерпретатор Bourne shell, в котором не поддерживаются функции (ю щ и псевдонимы (Ю.Ю), решить многие задачи, подобные описанным, позволят переменные shell и команда eval (S.10). Рассмотрим пример. Сначала приведем функцию shell scp (safe copy — безопасное копирование). Если файл назначения существует и не является пустым, эта функция, вместо того чтобы выполнить копирование, выведет сообщение об ошибке: зсрО { test44.20 if test ! -s "$2" then cp "$1" "52" else echo "scp: cannot copy $1: 52 exists" fi Если функцию scp дважды вызвать с одинаковыми аргументами, в первый раз будет создан файл bfile, а при второй попытке появится сообщение об ошибке: 5 scp afile bfile $ scp afile bfile scp: cannot copy afile: bfile exists Вот как выглядит функция scp, сохраненная не в виде функции, а в виде переменной shell: scp=' if test ! -s "$2" •then cp "51" "52" else echo "scp: cannot copy $1: 52 exists" ■f i i Поскольку эта псевдофункция использует параметры интерпретатора shell, нужно произвести еще один шаг: установить параметры. Чем проще функция, тем проще ее-использовать: set 44.19 5 set afile bfile $ eval "$scp" $ eval "$scp" scp: cannot copy afile: bfile exists - JP * В Bourne shell функция (шляется конструкцией типа список оз.щ. В bash точки с запятой можно опустить, но тогда функция не будет переносимой. Псевдонимы команд 169
и Уроки истории 11.01 Чему учит история? Как говорится, "история учит тому, что не учит ничему". К счастью, старый афоризм, гласящий, что "история склонна повторяться", вполне применим к UNIX. Большинство интерпретаторов обладают мощным механизмом формирования перечня ранее введенных команд (в дальнейшем просто перечень), который позволяет повторно выполнять предыдущие команды, а также редактировать прежнюю командную строку перед выполнением. Это особенно удобно в случае применения длинных и сложных команд. Чтобы активизировать перечень в С shell, достаточно включить в файл xshrc следующую строку: set history=*n где п — количество выполненных команд, которые будут сохранены. В ksh и bash для этого предназначена переменная HISTSIZE, которая устанавливается автоматически и значение которой равно 128 и 500 соответственно. . Команды в перечне имеют идентификационные номера. [В csh и bash существует команда history, позволяющая отобразить определенное количество последних команд. Например, команда history 20 выводит последние 20 команд. — JP\ (Кроме того, shell можно сконфигурировать так, чтобы номер каждой команды в перечне выводился в строке приглашения (7.02).) В csh и bash предыдущую команду можно повторить, если ввести ее номер, которому предшествует восклицательный знак. Допускается также повторное выполнение части командной строки, а также ее изменение при помощи различных средств редактирования. Параграфы 11.07 и 9.06 содержат краткий обзор различных приемов манипулирования перечнем. Да и вся глава содержит множество разнообразных советов относительно механизма подстановки команд из перечня. В большинстве интерпретаторов, за исключением Bourne shell и С shell, также предоставляются средства для интерактивного редактирования командной строки (п.п). [Интерактивное редактирование может показаться более привлекательным, чем ввод команд ! vi и 1рг ! $. Однако, изучив различные подходы, вы обнаружите множество ситуаций, когда ввод командной строки с использованием восклицательного знака быстрее и удобнее, чем интерактивное редактирование. — JP] - TOR 170 Часть вторая. Поручите грязную работу компьютеру
11.03 11.02 Перечень введенных команд: краткое резюме С shell и bash могут сохранять копии введенных вами командных строк. Впоследствии можно запросить некоторые или все копии, что позволит сэкономить время, затрачиваемое на ввод командной строки. Этот механизм называется подстановкой из перечня. Такая подстановка выполняется, когда командная строка начинается с восклицательного знака: ! команда. Подстановку из перечня можно сравнить с подстановкой значений переменных ($имя переменной) (б.о8) или подстановкой результатов выполнения команд С команда') (9j6): shell заменяет введенные вами символы (например, ! $) другими (в данном случае — предыдущей командной строкой или ее частью). Параграф 11.01 содержит вводную информацию о механизме работы с перечнем. В параграфах, перечисленных ниже, описаны многочисленные способы подстановки команд из перечня. • В нескольких следующих параграфах (11.03, 11.04, 11.05, 11.06) разные авторы описывают свои любимые способы использования перечня ранее введенных команд. • В параграфе 11.07 освещены все способы выполнения подстановок из перечня. Теория подкреплена многочисленными примерами. (Ранее, в параграфе 9.06, мы привели примеры относительно применения таких операторов csh и bash, как .г и др. Многие описанные там способы можно использовать при редактировании подставляемых из перечня командных строк.) • Удобный способ повторения последовательности команд в csh и bash описан в параграфе 11.08. • Каждый экземпляр интерпретатора shell ведет свой перечень. Как передать его из одного экземпляра shell в другой, описано в параграфах 11.11 и 11.12. • При выполнении подстановки из перечня не обязательно использовать восклицательный знак. В параграфе 11.15 показано, как применять другие символы. • Кот shell иначе управляет перечнем. Параграф 11.13 представляет собой введение в данную тему: в нем рассказывается, как редактировать командную строку в. ksh и bash. И последнее замечание: поместив порядковый номер команды в строку приглашения (7.02), вы облегчите повторное использование командных строк, которые еще видны на экране. - JP 11.03 Я предпочитаю !$ Работая в С shell, я настолько часто использую символы !$, что они теперь кажутся мне "неразлучными". Они означают: взять последний аргумент предыдущей командной строки. Данную комбинацию символов можно применять и в bash. Поскольку в большинстве команд UNIX последним указывается имя файла, можно ввести его только один раз, а в следующих строках использовать символы ! $. Приведем несколько примеров. • У меня есть много сжатых (24.о?) tor-архивов (i9.os>. Я извлекаю их посредством следующих команд: % gunzip groff.1.05.tar % tar xvf !$ tar xvf groff.1.05.tar Тот же прием используется при работе с архивными файлами, закодированными при помощи утилиты uuencode (19.02). Он подходит и для проверки правописания в текстах, отредактированных с помощью программы W: % vi fred.letter.txt % spell !$ spell fred.letter.txt Уроки истории 171
11.04 • Часто возникает необходимость переместить файл в другой каталог, а затем перейти в этот каталог. Сочетание символов ! $ позволяет также создать ссылку на имя каталога: % rav grmacs.tar /usr/lib/tmac % cd !$ cd /usr/lib/tmac -AN 1104 Я прецпочитаю !:п* Я часто применяю оператор ! $ (п.оз). Но моим любимым оператором подстановки из перечня является \:п*, где п — число от 0 до 9. Он означает: взять из предыдущей командной строки все аргументы, начиная с номера л и до конца. Поскольку я использую, как правило, несколько аргументов (обычно — имена файлов), это позволяет мне вводить их только один раз. Например, чтобы использовать систему управления версиями RCS ро.н) и вызвать для редактирования файлы параграфов данной книги с именами 1171, 6830 и 2340, я делаю следующее: % со -1 1171 6830 2340 RCS/1171,v —> 1171 RCS/2340,v —> 2340 revision 1.8 (locked) done % vi !:2* vi 1171 6830 2340 3 files to edit % ci -m"Chaoged TERM acref." !* ci -m"Changed TERM xref." 1171 6830 2340 В первой командной строке (со) я ввел имена файлов как аргументы 2, 3 и 4. В следующей командной строке (vi) я использовал оператор ! : 2*, который взял из предыдущей командной строки все аргументы от второго до последнего (в данном случае ^ до четвертого). В результате получилась командная строка с тремя именами файлов в качестве аргументов 1, 2 и 3. Поэтому в третьей командной строке (ci) я применил оператор !*, чтобы извлечь из предыдущей (второй) командной строки все аргументы от первого до. последнего. (!* является сокращением от !:1*.) Аргументы можно также брать из других командных строк. В частности, оператор !ет:2* извлекает аргументы от второго до последнего из предыдущей командной строки (начинается е em), предназначенной для запуска редактора emacs (см. параграф 11.07). Только на первый взгляд все это кажется сложным. Главное, чтобы вы умели правильно определять номер первого из необходимых вам аргументов. Я долго не использовал этот прием: Впоследствии он позволил сэкономить столько времени, что мне жаль, что я не изучил его раньше! - JP 11.05 Я предпочитаю ЛЛ Может быть, это и не самый мой любимый способ подстановки, но я его использую наиболее часто. Он особенно удобен, если у вас неуклюжие пальцы или вы работаете на непривычной клавиатуре: % cat myflie '■'. cat: myflie: No such file or directory ■■...% AliAil cat myfile 172 Часть вторая. Поручите грязную работу компьютеру
11.07 Достоинства этого приема проявляются наиболее ярко при вводе длинных командных строк. Я также использую оператор АА с оператором :р (п.ю) для повторного вызова предыдущей команды с возможностью ее изменения. Например: % !го:р more gobbledygook.c % *kAk2 more gobbledygook2.c Иногда сложности связаны не с количеством движений при вводе команды, а с тем, что ее трудно вспомнить. Мы хотим набрать имя файла, но никак не можем припомнить, как именно он называется... [Моя клавиатура может ни с того ни с сего начать дублировать символы. Я использую оператор Л для удаления лишних символов. Например: % lpr sources/aproggg.c lpr: sources/aproggg.c: no such file or directory % *gg lpr sources/aprog.c Можно было набрать AggA, но второй символ А не нужен. Если этот символ один, выполняется не замена части строки, а ее удаление. — JF\ - TOR 11.06 Использование оператора !$ для безопасной работы с метасимволами Все мы знаем, что перед командой rm с метасимволами нужно использовать команду Is, которая позволяет убедиться в удалении только того, что действительно должно быть удалено. На самом деле это не решает проблемы: можно набрать команду Is а*, а затем по ошибке — rm s* (достаточно одного неверного движения пальца). А вот следующий прием в csh и bash работает всегда: % Is a* al a-2 аЗ % rm !$ (Команда Is -d a* (i6M) выводит список подкаталогов.) Использование механизма подстановки из перечня для получения аргументов предыдущей командной строки — верный способ избежать ошибок. - ML 11.07 Подстановка из перечня [Хотя для демонстрации приемов подстановки из перечня используется команда echo, в действительности эти приемы чаще применяются с другими командами. — JP] В С shell восклицательный знак по умолчанию (u.is) является символом подстановки из перечня. (В bash он также используется.) Этот символ позволяет многократно вызывать введенные ранее команды и выполнять их, не повторяя набор. Количество сохраненных команд зависит от вас. Чтобы установить его, введите в свой файл конфигурации shell (2.02) строку, подобную следующей: set history=40 ...С shell HISTSIZE=40 ...bash, ksh В результате С shell будет сохранять последние 40 командных строк. Чтобы вывести список этих 40 команд, введите следующее: % history В csh и bash вы сможете отобразить только последние 10 команд, если выполните команду history 10. В С shell команда history -r позволяет вывести список команд в обратной последовательности. Уроки истории 11 1 I-L 173
11.07 Восклицательный знак можно использовать в командной строке несколькими способами. Может показаться, что некоторые из следующих примеров не заслуживают внимания, однако они демонстрируют способы обработки аргументов командной строки в псевдонимах команд (Ю.оз). Рассмотрим эти примеры. • Оператор ! ! повторяет предыдущую команду. • Оператор ! : повторяет предыдущую команду. Эта форма используется, если нужно добавить модификатор (9.ов), например: % echo ху ху % !:s/xy/yx echo yx ух Второй знак восклицания опущен. • Оператор ! so повторяет предыдущую команду, которая начинается с so. • Оператор !?fn? повторяет ту предыдущую команду, в которой встречаются символы fn. Искомые символы могут находиться и в аргументе, и в имени функции. Этот оператор отличается от оператора ! f п, который ищет символы f n в имени команды. (Последний знак вопроса не обязателен. Оператор ! ?fn эквивалентен оператору !?fn?.) • Оператор ! 34 выполняет команду с номером 34. Поиск в перечне команды с нужным номером осуществляется с помощью команды history или путем вывода в строке приглашения номера, который был присвоен данной команде в перечне р.т). • Оператор ! ! & добавляет в конец предыдущей командной строки амперсанд (&), обеспечивающий выполнение команды в фоновом режиме. В конец предыдущей командной строки можно добавить все что угодно. Например: % c»t -v foo % !! I more cat -v foo | more В данном случае shell повторит предыдущую команду, добавив канал к программе постраничного вывода тоге (25.оз). Часто оператор ! ! используется следующим образом: % cat -v foo % !! > out cat -v foo > out Здесь предыдущая команда повторяется, но вывод перенаправляется в файл. • Оператор !: 0 выбирает только имя предыдущей команды, а не всю командную строку: % /usr/bin/grep Ah fnl % !:0 Bh fn2 /usr/bin/grep/ Bh fn2 Обратите внимание на то, что оператор : О (9.019 может быть добавлен и к оператору подстановки из перечня. Например, оператор ! ! : 0 выдает имя последней команды, а двоеточие с числом, отличным от нуля, обеспечит получение аргумента с соответствующим порядковым номером. Например: % cat fn fnl fn2 % more ! : 3 more fn? 174 Часть вторая. Поручите грязную работу компьютеру
11.07 • Оператор ! :2-4 извлекает из предыдущей командной строки аргументы от второго до четвертого или аргументы с любыми номерами, которые вы укажете вместо 2-4: % echo 12 3 4 5 12 3 4 5 % echo !:2-4 echo 2 3 4 2 3 4 • Оператор ! :-3 извлекает из предыдущей командной строки аргументы от нулевого до третьего или аргументы с любыми номерами, которые вы укажете: % echo 12 3 4 12 3 4 % echo !:-3 echo echo 12 3 echo 12 3 • Оператор ! Л выбирает в предыдущей командной строке первый аргумент. То же выполняет оператор ! : 1. Запомните, что подобно тому, как в регулярных выражениях (26.Ы) символ А является признаком начала строки, оператор !Л выбирает начальный аргумент предыдущей командной строки. % cat fn fnl fn2 % more !A more En • Оператор ! $ выбирает в предыдущей командной строке последний аргумент. Точно так же в регулярных выражениях символ $ является признаком конца строки. Например: % cat fn % more !$ more fn Команде more передается последний аргумент предыдущей команды. • Оператор ! * извлекает из предыдущей командной строки аргументы от первого до последнего. Этот оператор широко используется в псевдонимах: % echo 12 3 4 5 12 3 4 5 % echo !* echo 12 3 4 5 12 3 4 5 В псевдониме alias vcat 'cat -v \!* I more' выходные данные команды cat -v (2s.o?) передаются команде more. Обратная косая черта необходима здесь для того, чтобы "спрятать" от shell символ подстановки из перечня (!), пока команды, заданные в псевдониме, не будут выполнены (см. параграф 10.03). • Оператор ! :2* извлекает из предыдущей командной строки аргументы от второго до последнего: % echo 12 3 4 5 12 3 4 5 % echo !:2* echo 2 3 4 5 2 3 4 5 Уроки истории 175
11.07 • Оператор ! :2- похож на ! :2*, однако он отбрасывает последний аргумент: % echo 12 3 4 5 12 3 4 5 % echo !:2- echo 2 3 4 2 3 4 • Оператор !?fn?% возвращает первое слово, содержащее символы fn: % sort fnl fn2 fn3 % echo !?fn?% echo fnl fnl В данном случае символы fn были найдены в слове fnl. Идем дальше: % echo 12 3 4 5 12 3 4 5 % echo !?ec?A echo l 1 В результате выбрана команда, имя которой содержит символы ее. Символ А обеспечил передачу первого аргумента этой команды. Есть еще один вариант: % echo fn fnl fn2 fn fnl fn2 % echo !?fnl?A !$ echo fn fn2 fn fn2 Эта загадочная команда указывает shell найти команду, имя которой содержит символы fnl, и выбрать ее первый аргумент (А). Затем команде передается последний аргумент (! $). • Оператор АхуАух представляет собой сокращенную запись команды подстановки (п.т, под. Например: % echo xxyyzzxx xxyyzzxx % АххАаЬ echo abyyzzxx abyyzzxx Здесь вместо символов хх подставляются символы ab. Этот оператор облегчает редактирование командной строки. • Оператор ! ! : s/xy/ab выполняет те же действия, но вместо символа А использует оператор подстановки (:s). Оператор :s может применяться для любой из предыдущих команд, например: % more afile bfile % echo xy xy % !m:s/d/c/ more afile cfile В данном случае применение косой черты не является обязательным требованием. Разделителем может служить любой символ: % !!:s:xy:yx 176 Часть вторая. Поручите грязную работу компьютеру
11.08 Как видите, мы использовали двоеточия [они очень удобны, если редактируемое слово содержит косую черту — JP\. Чтобы дополнить строку, сначала введите символ &, а затем — символы, которые нужно добавить: % echo xy ху % !!:s/xy/Syx echo хуух хуух Символ & в заменяющей части оператора заставляет его поисковую часть выдать результат поиска — ху. Левая, поисковая, часть оператора не может содержать метасимволы (2в.оз). Нужно вводить именно те символы, которые следует найти. В предыдущем примере заменяется только первая пара символов ху. Выполнить глобальную замену позволит оператор g. % echo ху ху ху ху ху ху ху ху % !!:а/ху/узс echo ух ху ху ху ух ху ху ху % !!:gs/xy/yx echo ух ух ух ух ух ух ух ух В данном случае оператор g обеспечивает глобальную замену ху на ух. Конечно, странно, что этот оператор предшествует оператору s. Рассмотрим еще один пример: % echo ху ху ху ху ху ху ху ху % !!:а/ху/ух echo ух ху'ху ху ух ху ху ху % !!:gs echo ух ух ух ух ух ух ух ух Мы поручаем shell провести глобальную (:д) замену в предыдущей команде (echo): вместо всех слов, совпадающих с шаблоном, подставляется последняя заменяющая строка (с помощью оператора &). Без оператора g интерпретатор shell всего лишь заменил бы еще одно слово ху. [В каждом слове глобальная замена осуществляется только однажды: ъ echo xyzzy xyzzy % !!:ga/y/p echo xpzzy xpzzy Здесь заменен только первый символ у. — ТС] - DR 11.08 Повторение последовательности команд Операторы подстановки из перечня (! !) позволяют получить копию предьщущей командной строки. Чаще всего они используются для повторного выполнения команды. Иногда требуется повторить последовательность команд. Для этого я два раза подряд ввожу команду ! -2 (выбор предпоследней команды): % vi plot u 1 ; '"■ ~ ~—~~~^ " —r_ mm истории 177
11.09 % vtroff -w plot % !-2 vi plot % !-2 vtroff -w plot Команда !-3 позволяет повторить последовательность из трех команд, команда !-4 — из четырех и т.д. Это очень удобно, ведь если вы умеете считать, вам не нужно вспоминать, что делать дальше. :-) - JP 11.09 Выполнение последовательности команд обработки файла [В некоторых случаях перечень не является наилучшим средством повторного выполнения команд. В этом параграфе Джерри приводит примеры, в которых несколько тщательно подобранных пользовательских команд применяются к одному и тому же файлу. В этом случае набрать краткий псевдоним проще, чем вызвать команду из перечня. — TOR] При написании параграфов для этой книги мне приходилось просматривать один за другим ряд файлов и применять к некоторым из них определенные команды. Было невозможно заранее определить, для каких файлов какие команды понадобятся и в какой последовательности. Поэтому я создал несколько временных псевдонимов. (В интерпретаторах типа sh для этой цели подошли бы функции (Ю.оя).) Большинство таких псевдонимов соответствовало командам системы управления версиями (RCS) (2о.и), но можно было бы использовать любые другие команды UNIX (для запуска компиляторов, отладчиков, программ печати и т.д.). % alias h 'set f="\!*";co -p -q "$f" | grep NOTE' % alias о 'со -1 "$f-■ % alias v 'vi "$f" % alias i 'ci -ro"FiJced title." "$f" Пользовательская команда h служит для сохранения имени файла в переменной shell (6.iis>. Впоследствии для данного файла выполняется команда grep. Приятно то, что после выполнения этой команды мне не нужно повторно набирать имя файла. Другие команды получают имя файла из переменной $f: % h ch01_summary NOTE: Shorten this paragraph: % о RCS/ch01_sumnary, v —> ch01_summary revision 1.3 (locked) done % v " ch01_summary" 23 lines, 1243 characters При вводе псевдонима h запоминается имя нового файла. Если вы все время применяете одни и те же команды обработки файлов, целесообразно создать для них один псевдоним: % alias d 'set f="\!*"; со -1 "$f" SS vi "$f" SS ci "$f" % d ch01_sunnnary Оператор &£ (два амперсанда) (44.09) означает, что следующая команда не будет выполняться, если предыдущая команда не возвратит код 0 ("успех"). Вместо амперсандов вы можете использовать точки с запятыми (S.os). - JP 178 Часть вторая. Поручите грязную работу компьютер!
11.11 11.10 Просмотр перечня ранее введенных команд Далее вы узнаете, как проверить перечень перед использованием. Но прежде вспомним, каковы функции операторов подстановки из перечня в csh и bash. • Операторы ! / и ! f га заменяются последней командной строкой, которая начинается с / и fra соответственно. • Оператор ! ?af ? заменяется последней командной строкой, содержащей в любом месте строку af. Но если вы, как и я, не можете положиться на свою память, то ручаться, что оператор ! ?af ? возвратит нужную команду, невозможно. Для выполнения соответствующей проверки добавьте в конце оператор :р. Shell выведет командную строку с подставленными значениями, но выполнять ее не будет. Если результат вас устраивает, введите ! !, чтобы выполнить команду. Например: % !?af?:p lp afile bfile cfile % ! I lp afile bfile cfile request id is 676 В первой строке оператор :р указывает, что предыдущая командная строка будет просто выведена на экран. В строку, введенную по второму приглашению, я не включил оператор : р, и команда lp была выполнена. Оператор :р работает со всеми операторами подстановки из перечня, а не только с оператором !?...?. - JP 11.11 Сохранение перечня ранее введенных команд Чтобы перечень сохранялся даже после выхода из системы, значение переменной С shell (t.os) savekist должно быть равным количеству команд, которые нужно сохранить. В Кот shell и bash устанавливать переменную не нужно: они автоматически сохраняют перечень. Однако, если вы захотите самостоятельно определить количество строк, сохраняемых в перечне bash, установите соответствующее значение для переменной среды HISTFILESlZE. В Korn shell переменная HISTFILESlZE задает количество команд, сохраняемых не только текущим интерпретатором shell, но и другими экземплярами интерпретатора. При выходе из системы заданное количество строк из перечня csh сохраняется в файле .history вашего начального каталога. Bash и ksh используют имя файла, указанное в переменной среды HISTFILE. По умолчанию в bash применяется файл .bash_history, а в ksh — .shjiistory. В современных многооконных системах все немного сложнее. На устаревших терминалах пользователи обычно запускают один главный shell, поэтому они могут установить переменную, задающую параметры сохраняемого перечня, в файле .login или .profile и использовать ее в своем регистрационном интерпретаторе shell. Однако в многооконных системах, таких как X Window, или в сетевых файловых системах, распределяющих начальный каталог между несколькими серверами, различные интерпретаторы shell сохраняют перечень в одном и том же файле. Некоторые рекомендации на этот счет приведены в следующих параграфах. Bash и ksh Организовать отдельные файлы для хранения команд каждого экземпляра Korn shell или bash можно следующим образом. Настройте свой файл конфигурации (2.02) таким образом, чтобы для каждого сервера или окна устанавливались различные значения переменной HISTFILE. Для этого используйте такие имена файлов, как $HOME/.sh_history.windown и ~/.bash_his- tory-имя-сврввра. Если при каждом входе в систему будет выполняться Одна и та же процедура инициализации, то каждому окну и серверу будет предоставлен тот же перечень, что и при последнем вызове (см. параграфы 2.12 и 2.13). Уроки истории 179
11.11 Если каждый раз открывается произвольное количество окон, необходимо уделить более пристальное внимание вопросу автоматической привязки файла с перечнем команд к интерпретатору shell при следующем входе в систему. От вас потребуется разработать собственную методику. Наиболее простой рецепт — использовать значение j>£ (&.щ (которое будет разным для каждого экземпляра shell) в качестве уникальной части имени файла. Это можно реализовать двумя способами: HISTFILE=/tmp/sh_hist.$$ HISTFILE=$HOME/.sh_hist.$$ В первом примере использован каталог для временных файлов (в параграфе 21.03 описан способ его очистки). Если системный каталог /tmp чистится часто и если файлы с перечнем команд хранятся в этом каталоге, система может уничтожить их. Проконсультируйтесь по этому вопросу с системным администратором. Помните: если не установить защиту файлов с помощью команды umask (22.04), ваш файл с перечнем команд сможет читать любой пользователь (22.02). Если это имеет для вас значение, создайте временные файлы в своем начальном каталоге (или в другом защищенном каталоге), как показано во втором примере. В параграфе 3.04 рассказано, как удалять такие файлы. Сделаем еще ряд замечаний. • Кот shell постоянно поддерживает файл с перечнем команд, добавляя в него новые строки при выполнении каждой команды. Следовательно, поскольку используется один и тот же файл, указанный в переменной HISTFILE, перечень является общим для всех текущих интерпретаторов shell. Это имеет как положительные, так и отрицательные стороны. • При использовании bash каждый интерпретатор создает в памяти свой перечень. Этот перечень не записывается в файл (имеющий имя, указанное в переменной HISTFILE каждого интерпретатора shell), пока данный интерпретатор существует. Команда history -w позволяет выполнить принудительную запись перечня. Файл с командами перечня (или другой файл, содержащий командные строки) можно прочесть в текущем интерпретаторе bash с помощью команды history -г. Еще один пример вы найдете в параграфе 11.12. С shell В С shell файл с командами перечня может иметь только имя .history. Если переменную С shell savehist установить для каждого окна отдельно (например, в файле .cshrc), каждый экземпляр shell будет записывать свой перечень в файл .history, что может вызвать проблемы. Даже если больших проблем не возникнет, получение перечня из всех окон или со всех серверов может не отвечать вашим ожиданиям. Конечно, вы можете вручную установить переменную savehist только в том рабочем окне, в которое собираетесь вернуться. Но есть другой способ: активизация команды С shell history -h (выводит перечень без ведущих номеров, вследствие чего команды перечня можно использовать без дополнительной обработки) и переадресация ее вывода в файл. Затем следует выполнить команду source -h, чтобы при следующем входе в систему загрузить содержимое файла в перечень. Хотите автоматизировать эту процедуру? Во-первых, выберите принцип формирования имен файлов, например ~/.history.windown или ~/Мйогу.тшя_сер-вера, чтобы закрепить за своим окном или сервером соответствующий файл с командами перечня. Если каждый из ваших интерпретаторов С shell является регистрационным (2.os), можно выполнить команду history -h из вашего файла .logout или команду source -h из файла .login* В случае использования неинтерактивных интерпретаторов shell осуществить автоматизацию труднее. Можно прибегнуть к таким способам: • Установите переменную ignoreeof (з.о5), чтобы из системы можно было выйти только с помощью команды exit (З8.04). * В X Window cut) надлежит применять команду xtam -Is. 180 Часть вторая. Поручите грязную работу компьютеру
11.13 • Создайте псевдоним для команды exit (woe), который перед выходом из системы выполняет Команду history -h. • Выполняйте из своего файла xshrc команду source -h. Используйте условную конструкцию if ($?prompt) (2.09), чтобы обеспечить выполнение этой команды только в интерактивных интерпретаторах shell. Если вы решите время от времени вручную выполнять команды history -h и source -h, обеспечить сохранение и восстановление тех команд, которые вас интересуют, можно с помощью сценария (п.в). - JP, TOR 11.12 Передача перечня ранее введенных команд другому экземпляру shell С shell, Korn shell и bash автоматически сохраняют список вводимых вами команд. В перечень csh и bash можно добавлять новые команды, не набирая их повторно. Для чего это нужно? • Вам может понадобиться набор команд, которые можно было бы вызывать и выполнять при каждом входе в систему. Возможно, это окажется более удобным, чем использование псевдонимов, поскольку не нужно думать об их уникальности. Кроме того, это удобнее сценариев, особенно если: необходимо выполнять определенный набор команд, но в различной последовательности. • Это может понадобиться при запуске нескольких интерпретаторов shell (скажем, в нескольких окнах) и при необходимости передачи перечня из одного процесса в другой (ни). Приведем пример. При помощи команды history -h (С shell) или history -w (bash) сохраните перечень в файле. Отредактируйте этот файл, чтобы исключить команды, которые вас не интересуют. С shell bash % mail -в "My report" bigboss % history -h > history.вtd % vi history.вtd ...Удалить ненужные команды... Загрузите файл в перечень при помощи С shell ' % source -h history.вtd $ history -r history.std %• !ma . $ !ma mail -s "My report" bigboss mail -s "My report" bigboss Конечно, в bash'к этим командам можно применить средства интерактивного редактирования командной строки (шз). - JP 11.13 Редактирование командной строки [Материал этого параграфа рассчитан на Korn shell. Полученные сведения пригодятся вам и в том случае, если вы работаете с bash или tcsh, хотя тогда будут иметь место некоторые различия. Сверьтесь с документацией по своему интерпретатору shell. — JP] Если при вводе командной строки вы допустили ошибку и нажали [RETURN], строка пропадет. Получив досадное сообщение об ошибке, наберите все сначала, если только вам не посчастливилось работать с С shell и запомнить его невероятные команды подстановок из перечня (п.о2). Кот shell, как вам, возможно, известно, предоставляет возможность редактирования командной строки в стиле редактора vi. Если испытаете это на практике, то согласитесь, что vi не очень подходит для редактирования командной строки. [Я не согласен. :-) Когда курсор находится в командной строке, нужно нажать клавишу [ESC], чтобы перейти в режим редактора vi. Команды в и /, как Обычно, устанавливают режим ввода. $ mail -в "My report" bigboss $ history -w history.std $ vi history.std ...Удалить ненужные команды... команды source -h (csh) или history -r (bash). bash Уроки истории 181
11.13 В любом режиме достаточно нажать клавишу [RETURN], чтобы выполнить командную строку, или клавиши [CTRL-c], чтобы отменить ее действие. — Щ Однако немногие знают, что Кот shell имеет еще один режим редактирования командной строки, эмулирующий редактор Emacs (зг.оо. Этот режим представляет собой естественное расширение набора команд редактирования интерпретатора shell (например, [Delete] или [Backspace]), поэтому даже если вы не знакомы с редактором Emacs, то все равно обнаружите, 4tq данный режим полезен. Чтобы установить режим emacs, вставьте в файл .profile следующую строку: set -о emacs Мы рассмотрим только самые полезные команды режима emacs. Более полное описание вы найдете в книге Билла Розенблата (Bill Rosenblatt) Learning Korn Shell (издательство O'Reilly & Associates). В режиме emacs доступны клавиши удаления символа (SM) и команды редактирования строк. Наиболее важные из них описаны в табл. 11.1. Таблица 11.1. Команды редактирования строк в режиме emacs (Korn shell) Команда Функция CTRL-b CTRL-f CTRL-d CTRL-a CTRL-e CTRL-k CTRL-w CTRL-y CTRL-c Перемещение на один символ влево (без удаления) Перемещение на один символ вправо Удаление одного символа Перемещение в начало строки Перемещение в конец строки Удаление символов до конца строки Удаление символов до начала строки Восстановление предыдущего удаленного элемента Удаление всей строки В режиме emacs поддерживается файл перечня, что позволяет повторно вызывать предыдущую команду. В табл. 11.2 описаны самые важные команды навигации по перечню. Таблица 11.2. Команды для работы с перечнем в режиме emacs Команда Функция CTRL-p CTRL-n CTKL-гстрока Переход к предыдущей команде Переход к следующей команде Поиск команды, содержащей указанную строку, выполняемый в обрат- ном направлении Наиболее полезна первая команда, с помощью которой можно вернуться к предыдущей команде и исправить ее. Команда поиска позволяет повторно вызывать некоторые недавно введенные команды, не просматривая строку за строкой при помощи команды [CTRL-p]. Достаточно ввести [CTRL-r], искомую строку и нажать клавишу [RETURN], чтобы Korn shell вернул самую последнюю команду из тех, что содержат указанную строку (или звуковой сигнал, если поиск не принес результатов). Если возвращена подходящая команда, для ее выполнения нужно еще раз нажать клавишу [RETURN]. Если в начале заданной строки стоит символ л, она будет соответствовать только тем командам, которые начинаются с указанной строки. Это знакомо пользователям таких утилит UNIX, как grep, sed и awk. Еще одна важная возможность режима emacs — дополнение имен файлов. Она должна быть знакома специалистам по С shell, а также пользователям редактора Emacs (см. параграф 9.08). В режиме emacs. есть две команды дополнения имен файлов. Более полезной является [ESC][ESC] (клавиша [ESC], нажатая дважды). Если при вводе слова нажать клавиши [ESC][ESC], интерпретатор shell будет искать файл, имя которого содержит указанное слово, и попытается дополнить это имя. Если с введенного слова начинается только одно имя, shell дополнит введенную часть имени. Если же таких имен несколько, будет дополнена только общая часть похожих имен. Допустим, текущий каталог содержит единственный файл programx. Вы ввели pro, а затем — [ESC][ESC]. Тогда shell автоматически дополнит имя файла и вставит дополнительный пробел. 182 Часть вторая. Поручите грязную работу компьютера
11.14 Но если в каталоге есть также файл program.о, имя будет расширено только до progam., и вам придется завершить его ввод. [Строки из перечня можно редактировать при помощи любого другого редактора: см. пара- ' граф 11.14. - JF\ - BR 11.14 Цругие способы редактирования строк из перечня Tcsh является версией csh, в которой предусмотрена возможность редактирования перечня. Если же вы не хотите переходить к работе с tcsh, сымитируйте редактирование перечня при помощи сценария redo. Работая с bash или ksh, вы, наверное, изучили возможности редактирования, предоставляемые вашими интерпретаторами, однако опыт показывает, что немногие знакомы с командой /с. С shell: сценарий redo В С shell сценарий redo выполняется при помощи команды source (44.23) в псевдониме г. Первоначальная версия, автор которой неизвестен, была размещена в Usenet в 80-е годы. В 1987 году Дейв Паттерсон (Dave Patterson) там же опубликовал приведенную ниже версию. Данный сценарий обеспечивает переход редактора ех в открытый режим (зом) редактирования буфера ввода. Этот режим знаком пользователям редактора v«. Вы можете просмотреть предыдущие 22 команды и нажать [RETURN], чтобы выполнить одну из них. Перед выполнением команду можно отредактировать при помощи редактора vi. Можно даже провести поиск нужных строк посредством символа /. Не забывайте только, что после ввода искомой строки нужно нажимать клавишу [ESC], а не [RETURN]. Прежде чем вы начнете работать со сценарием redo, инсталлируйте его, установив только разрешение на чтение и отменив право на выполнение (22.02)'. Затем создайте псевдоним, содержащий полное путевое имя (ы.Ю) сценария: alias r source -/.lib/redo При активизации эта команда читает команды из файла сценария, выполняемого с помощью команды source (io.os). Jf^% Приводим текст сценария redo. history -h 22 >! /tmp/redo.$$ # Записать символ CR в $с[1] и ESC в $с[2]: set c=('echo "m e" I tr me '\015\0331' # Установить соответствие между символом CR и командой :wq\.yi запустить It редактор ех для строк со 2-й по последнюю в открытом режиме (setenv EXINIT "map $c[l] :.wq\!$с[2]Imap! $с[1] ${с[2]} :.wq\!$с[2]";\ ех '+$-1 open1 /tmp/redo.$$) tail -1 /tmp/redo.$$ >! /tmp/cmd.$$ fr Вставить в перечень без выполнения source -h /tmp/cmd.$$ fr Уничтожить временные файлы /bin/rm -f /tmp/(cmd,redo}.$$ unset с fr Если для повторного выполнения вызвана сама команда redo, # не выполнять ее if (!-2:0 != ! ! :0) !! Наберите г, чтобы вызвать пользовательскую команду- Затем при помощи клавиш перемещения курсора (j, k) выберите строку, которую нужно отредактировать. Отредактируйте строку (при этом помните, что вы работаете в открытом режиме (зом) редактора ех). Закончив, нажмите [RETURN]. (He набирайте ZZ или q.) <9] echo ..33 45.3$ map 31.02 tail 25.14 !... /1.07 tyww истории 183
11.15 ksh и bash: команда fc Ksh и bash имеют встроенную команду ./с (сокращение от "fix command" — команда исправления ошибок). Она похожа на сценарий redo, но обладает более широким кругом возможностей. Далее приведены лишь основные сведения. За подробной информацией обратитесь к руководству по вашему интерпретатору shell или к соответствующему справочнику. » Для просмотра списка предыдущих команд следует использовать опцию -/: $ fc -1 19 Is -F 20 less expn.c 21 vi $_ 22 make 23 expn info@ora.com 24 fc -1 Чтобы получить краткий список, передайте команде fc начальный номер или имя, которые должны быть в списке. Например, команды f с -1 vi и fc -1 21 выводят четыре последние строки приведенного выше списка. Для редактирования предыдущих командных строк команда fc может вызвать редактор системы UNIX (vi, Emacs, pico и т.п.). Указать на конкретный редактор при вызове команды fc позволяет опция -е, но, наверное, проще сохранить имя редактора в переменной shell FCEDIT (в файле .profile или .bashrc (2.о2>). Чтобы в предыдущем списке отредактировать строки от vi до ехрп, наберите fc v e или f с 21 23. В результате запустится ваш редактор, получивший для редактирования три строки. Эти строки можно обрабатывать каким угодно образом: добавлять команды, перекомпоновывать и т.д. После выхода из редактора сохраненные командные строки будут выполнены. Если вы передумаете и не захотите выполнять ни одной команды, перед сохранением удалите в редакторе все строки или замените их холостыми командами типа echo hi. - LM, JP ИЛЬ Изменение символов подстановки команд из перечня в С shell Применение специальных символов (в частности !) может быть сопряжено с некоторыми неудобствами. Часто необходимо вводить командные строки, содержащие восклицательные знаки или, скажем, символы А. Если неправильно выделить эти символы, shell не поймет вас. При частом использовании специальных символов можно выбрать другой путь установки переменной histchars. Эта переменная содержит строку из двух символов. Первый символ заменяет восклицательный знак, а второй — символ Л (знак модификации (ii.os)). Например: % set histchars='@#' % Is file* filel file2 file3 % @@ Повторение предыдущей команды (было !!) Is file* filel file2 file3 % #file#data Редактирование предыдущей команды (было ^file^data*) Is data* data4 data5 Посредством переменной histchars можно задать любые символы, но лучше выбрать те, которые нечасто используются в командных строках. Приемлемыми являются символы #* и ,. - ML * При использовании интерпретатора С shell символ # является обозначением комментария (им) только в неинтерж- тинных формах интерпретатора. Использование его в качестве символа подстановки из перечня введенных команд не создаст конфликтной ситуации, поскольку в неинтерактивных sheD такие подстановки не разрешены. 184 Часть вторая. Поручите грязную работу компьютер]/
1L16 11.16 Альтернатива изменению символов подстановки из перечня Если в некоторых командах (обычно — в uucp и mail (из)) необходимо использовать восклицательный знак (или другой установленный вами символ подстановки из перечня), нужно перед каждым таким символом вводить обратную косую черту. Можно также временно запустить Bourne shell (если только вы не работаете с интерпретатором bash, заменившим Bourne shell). Любой из этих вариантов проще, чем изменение переменной histchars. Например: % mail ora\! iahtarV sally < filel Обратная косая черта перед восклицательным знаком % sh Запуск Bourne shell $ mail ora!ishtar!sally < filel Символ ! здесь не является специальным $ exit Выход из Bourne shell % Снова в С shell Настоящий Bourne shell не имеет возможностей подстановки из перечня, поэтому у восклицательного знака нет специального назначения. Это обычный символ. Между прочим, если у вас есть многооконная система, то вы можете копировать и вставлять команды вместо того, чтобы использовать перечень ранее введенных команд. -ML..
12 Управление заданиями 12.01 Основные сведения Многозадачность, т.е. возможность одновременного выполнения нескольких программ, — одна из самых выдающихся особенностей UNIX. При управлении заданиями необходимо заранее определить, какое время отвести для выполнения задания в интерактивном и в фоновом режиме (когда взаимодействовать с программой нельзя, а можно только досрочно прервать ее выполнение). В С shell, а также в других интерпретаторах, появившихся впоследствии (в том числе в некоторых новых версиях Bourne shell), имеются встроенные средства управления заданиями. Задание можно запустить и остановить, перевести из фонового режима в интерактивный и наоборот. В многооконных системах, которые позволяют одновременно отображать на экране несколько активных терминальных окон, возможности управления заданиями имеют меньшее значение. И все же в некоторых случаях управление заданием обеспечивает большую производительность, чем открытие нового окна. Данная глава содержит только вводную информацию, сама же тема управления заданиями гораздо шире. Правильную работу средств управления заданиями обеспечивает не только интерпретатор shell: необходима также поддержка ядра системы UNIX. Беркли-версии UNIX, начиная с BSD 4.0, обладали средствами управления заданиями, поэтому аналогичные системы UNIX также имеют эти средства. Многие версии UNIX System V, предшествовавшие Release 4, не предоставляли возможностей управления заданиями. Если ваша система UNIX не позволяет управлять заданиями, вы все равно сможете выполнять задание в фоновом режиме (см. последний параграф этой главы). Интерактивный и фоновый режимы В системе UNIX различают программы, выполняющиеся в интерактивном и в фоновом режиме. Благодаря этой особенности возможен одновременный запуск с терминала нескольких программ. Когда программа выполняется в интерактивном режиме, все, что вы вводите с клавиатуры, направляется в стандартный ввод программы при условии, что он не переадресован. В результате вы сможете взаимодействовать с программой, пока не завершилось ее выполнение. Если же программа выполняется в фоновом режиме, она не может получать ввод с клавиатуры. Все, что вы набираете, поступает в интерпретатор shell и воспринимается как команда. Следовательно, в фоновом режиме поддерживается одновременное выполнение нескольких программ. В интерактивном режиме может выполняться только одна программа. Чтобы запустить программу в фоновом режиме, поставьте в конце командной строки амперсанд (&). Например: % f77 program.F £ [1] 9145 % 186 Часть вторая. Поручите грязную работу компьютера
12.01 Эта команда запускает в фоновом режиме компилятор FORTRAN, что позволяет выполнять другую работу, пока производится компиляция. В ответ shell выводит номер задания в квадратных скобках и идентификатор процесса (PID) (зв.оз) данной команды. Затем интерпретатор запрашивает новую команду. При выполнении команды jobs выводится краткий отчет обо всех программах, выполняемых в фоновом режиме. Например: % f77 program.F 6 [1] 9145 % jobs [1] + Running til program.F % Перевести программу из фонового режима в интерактивный позволяет команда fg. При выполнении нескольких фоновых заданий после этой команды следует указать идентификатор задания — знак процента и номер задания: % jobs [1] - Running f77 program.F [2] + Stopped vi sinus.F % fg %1 Плюс в отчете команды jobs указывает, какое задание будет переведено в интерактивный режим по умолчанию (П.оз). Чтобы приостановить выполнение программы в интерактивном режиме, нажмите клавиши [CTRL-zj. [Это сочетание клавиш можно использовать для останова большинства зависших или неуправляемых программ до выяснения того, что с ними делать дальше. Команда [CTRL-z] позволяет также прервать программы, которые не могут быть остановлены такой командой прерывания (5.09), как [CTRL-c]. — JP\ В результате ввода команды bg выполнение остановленной программы продолжается в фоновом режиме. Команда fg восстанавливает выполнение остановленной программы в интерактивном режиме. Например: %£77 -о program program.F ICTKL-g] Stopped % bg [1] + Running f77 -o program program.F % После ввода команды /77 приглашение не появляется, так как компиляция выполняется в интерактивном режиме. После нажатия клавиш [CTRL-z] интерпретатор shell выводит слово Stopped, чтобы сообщить, что он прервал выполнение. В этом месте можно ввести любую команду. В данном случае команда bg позволяет продолжить выполнение задания в фоновом режиме. Такая возможность удобна, например, в том случае, если вы забыли набрать в конце командной строки & или решили запустить другую программу, пока выполняется задание. Чтобы прекратить выполнение фонового задания, Можно вместо идентификатора процесса использовать номер задания, соответствующий данной команде: % kill %i Если опустить знак процентов, UNIX будет интерпретировать номер задания как номер процесса. Им может оказаться номер процесса какой-либо функции операционной системы. UNIX не позволит вам допустить такую ошибку, если вы не являетесь суперпользователем (1.24). Ну а если вы — суперпользователь, такая команда окажется фатальной. Время от времени вы можете регистрироваться в системе как суперпользователь, поэтому будьте внимательны и не сделайте такого упущения. В течение следующих нескольких секунд нажмите несколько раз клавишу [RETURN]. Должно появиться следующее сообщение: [1] Terminated f77 -о program program.F Управление заданиями 187
12.02 Если вы его не увидите, воспользуйтесь командой jobs, чтобы проверить,, выполняется ли еще данное задание. Если да, то в качестве последнего средства используйте опцию -9. % kill -9 %1 [1] Killed f77 -о program program.F Опция -9 не позволяет процессу уничтожать свои временные файлы и обеспечивать "интеллигентное" завершение, поэтому не применяйте ее без надобности. Программа, выполняемая в фоновом режиме, не может читать ввод с терминала. Если для выполнения фонового задания понадобится терминальный ввод, будет сделана пауза, и команда jobs выведет такое сообщение: Stopped (tty input). Чтобы продолжить выполнение программы, следует перевести ее в интерактивный режим при помощи команды fg и ввести необходимые данные. Вы избавите себя от этих забот, переадресовав ввод программы таким образом, чтобы она читала все необходимые данные из файла. Можно также переадресовать стандартный вывод и стандартный поток ошибок. При невыполнении этого условия выходные данные, создаваемые программой, будут появляться на экране терминала (если не активизировать команду stty tostop 02.07)). Поскольку вы, вероятно, будете выполнять и другие команды, мелькание на экране посторонних данных и сообщений может внести путаницу. Если в системе отсутствуют средства управления заданиями, запустить программу в фоновом режиме позволит оператор 4. Такие системы не предоставляют возможности перевода задания в интерактивный режим и обратно. В них существует единственное средство, позволяющее установить, какие задания выполняются в фоновом режиме, — команда ps-w.os). — ML, из книги UNIX for FORTRAN Programmers издательства O'Reilly & Associates 12.02 Другие способы ссылок на задания Если в фоновом режиме выполняется несколько заданий, к ним можно обращаться по номерам, которые можно получить с помощью команды jobs (12.01). Например: % jobs [1] + Stopped vl TODO [2] - Running nroff -ms chOl % kill %2 % fg %1 Чтобы выбрать определенное задание, вместо номера можно указать имя задания. Для этого после знака процента наберите имя команды. Так, две последние команды из предыдущего примера могли бы иметь следующий вид: % kill %nroff % fg %vi Оператор %? позволяет задать любую уникальную часть командной строки задания. Кстати, в руководстве не сказано, что при использовании этого оператора может возникнуть необходимость поставить обратную косую черту (s.u) перед вопросительным знаком, поскольку он также является метасимволом shell. Если не сделать этого, может" появиться сообщение No match (нет совпадения). Есть два варианта команд для уничтожения задания nroff, приведенного в предыдущем примере: % kill %?ch01 Без обратной косой черты (в большинстве случаев) % kill %\?ch01 С обратной косой чертой (в некоторых случаях) Возможен еще ряд сокращений. Вместо команды .4?.и номера задания можно набрать только номер задания. Зачем набирать % fg %3 если команда % %з также-работает? 188 Часть вторая. Поручите грязную работу компьютера
12.04 Подобным способом можно перевести в фоновый режим остановленное задание. Например, команда % «2 & переводит в фоновый режим задание 2. Конечно, ввод команды fg или bg без номера задания позволит сэкономить время, если выполняется только одно задание или если вы хотите обратиться к текущему заданию. Единственная проблема в этом случае может быть связана с тем, что текущим является не то задание, о котором вы думаете (п.оз). - TOR 12.03 Какое задание является текущим? Символ % служит для обозначения текущего задания, которым может быть остановленное или выполняющееся фоновое задание. При остановке любого задания текущим является задание, остановленное последним. Если задания не останавливались, то текущим считается последнее фоновое задание. Попробуйте прервать работу редактора (например, vi), а затем перевести в фоновый режим другое задание: % vi afile |CTRl-z| Stopped sleep 40.02 % sleep 1000 6 [2] 12345 % fg Вы увидите, что команда f g переведет в интерактивный режим именно редактор vi. - JP 12.04 Экономия времени при редактировании Я не раз сталкивался с фактом применения последовательности команд, подобных приведенным ниже. Программисты выполняют эти команды при написании и компиляции программ, а писатели — при создании черновиков текста и прогоне их через программу форматирования. Не уходит ли на это слишком много времени? % vi файл . ..Редактирование файла и выход из редактора vi... % программа файл ...Обработка файла... % vi файл . ..Повторное редактирование файла... % программа файл ...Повторная обработка файла... При каждом запуске редактора vi они вынуждены переустанавливать опции и перемещать курсор на прежнее место. После перезапуска редактор vi не помнит предыдущую строку поиска (команду я), предыдущее действие (команду .), предыдущее регулярное выражение, а также именованные и неименованные буферы. Все эти проблемы можно решить, если в системе имеются средства управления заданиями (12.08)* Вместо того чтобы выходить из редактора vi, перейдите в командный режим и сохраните содержимое буфера при помощи команды :w. Остановите редактор посредством команды [CTRL-z]. Обработайте файл. Когда снова можно будет приступить к редактированию, переведите редактор vi в интерактивный режим с помощью команды fg. Редактор будет в том же состоянии, что и прежде. * Если проблемы не удастся решить с помощью описанных методов, можно прибегнуть к временному выходу в shell (Шб). Управление заданиями №
12.05 Если в редакторе vi установить опцию autowrite, в случае изменения текста редактор автоматически сохранит содержимое буфера при нажатии клавиш [CTRL-z]. Тогда не придется беспокоиться о том, чтобы не забыть выполнить команду :w перед остановкой редактора. Опцию autowrite можно установить по приглашению (:), однако я устанавливаю ее в своем файле .ехгс (зом). [Не обязательно сохранять содержимое буфера перед приостановкой редактора vi. Наши рекомендации хороши, но механизм управления заданиями не требует их выполнения. При нажатии клавиш [CTRL-z] редактор приостановится независимо от того, сохранили вы свои файлы или нет. — TOR] - JP 12.05 Остановка отдельных заданий при перегрузке системы Если работа вашего компьютера сильно замедлилась, можно уничтожить (за.щ некоторые процессы, которые впоследствии, вероятно, потребуется запустить опять. Беркли-системы позволяют изменять приоритеты выполнения процессов (renice) (39.1i). Однако после этого (при нормальной скорости работы системы) невозможно повысить приоритет процесса, если только вы не являетесь суперпользователем (1.24). Если вам не нужны срочно результаты работы некоторых процессов, попробуйте остановить эти задания. Первыми кандидатами являются такие "пожиратели времени процессора", как программы форматирования текста (43.12), компиляторы (зг.ов), а также любые другие задания, которые часто фигурируют в отчетах команд ps (3s.os> и time (зя.ог). Запустите их позже, и они продолжат работу с точки останова. Если задание выполняется в интерактивном режиме, нажмите клавиши [CTRL-z] 02.01), чтобы остановить его. При выполнении фонового задания в интерпретаторе csh или tcsh используйте команду stop с идентификатором задания, например stop %3 или stop %cc. В других интерпретаторах shell — даже в тех, в которых отсутствуют средства управления заданиями, — можно использовать команду kil[ (за.Ю) вместе с сигналом -STOP и номером задания либо идентификатором процесса. Csh и tcsh имеют команду stop, которая выполняет все самостоятельно. В других интерпретаторах вы можете добавить в свой файл конфигурации (2.02) пользовательскую команду с именем stop. Впоследствии с помощью команды bg или fg переведите задание в фоновый или интерактивный режим. Например: bash$ alias stop='kill -STOP' bash$ jobs [1]+ Running g++ hugeprog.cc & bash$ stop %1 [1]+ Stopped (signal) g++ hugeprog.cc ...другие команды... bash$ %1 [1]+ g++ hugeprog.cc & - JP 12.06 Извещения об изменении состояния заданий Обычно интерпретатор shell сообщает об изменении состояния фоновых заданий при очередном выводе строки приглашения. Когда shell выводит новую строку приглашения, может появиться сообщение следующего вида: [1] +Stopped (tty input) rm -r В этой строке сообщается, что команда rm -r, выполняемая в фоновом режиме, ожидает ввода. Возможно, этой команде нужно указать, удалить ли файл, предназначенный только для чтения, или что-нибудь в этом роде. Обычно нас устраивает этот алгоритм, выполняемый 190 Часть вторая. Поручите грязную работу компьютеру
12.08 по умолчанию. Привязка сообщений к выводу новой строки приглашения позволяет минимизировать "засорение" экрана. Если же вы хотите немедленно получать извещения об изменении состояния задания, установите переменную notify: % set notify ... С shell $ set -о notify ...bash У всего этого есть один недостаток. Представьте: вы изучаете тщательно скомпонованные данные, заполняющие экран целиком, и тут неожиданно весь порядок нарушается многочисленными сообщениями интерпретатора shell. По этой причине большинство пользователей предпочитают держать переменную notify выключенной (unset). Чтобы остановить вывод фонового процесса, используйте команду stty tostop (12.07). - ML 12.07 Остановка вывода фонового процесса Если запустить задание в фоновом режиме (1.26) и не переадресовать (is.oi) его вывод, то информация, которую выводит данное задание на стандартный вывод и в стандартный поток ошибок, попадет на экран. Эти сообщения могут засорить экран во время выполнения другой программы. Кроме того, важные сообщения могут быть вытеснены за пределы экрана. Существует также риск того, что фоновая программа очистит экран и сотрет все сообщения. В большинстве систем BSD UNIX, как и во многих новых версиях UNIX с элементами управления заданиями, имеется команда stty tostop. Вводите эту команду по приглашению или же вставьте ее в свой файл .login или .profile (2.01).* В результате фоновые процессы, пытающиеся выводить данные на терминал, будут остановлены. Чтобы просмотреть вывод фоновых процессов, переведите их в интерактивный режим (с помощью команды fg). Как узнать, что фоновый процесс остановлен? Перед выводом приглашения shell делает такое сообщение: [1] + Stopped (tty output) имя_задания % Этим сообщением интерпретатор shell может прервать интерактивное задание, как только будет остановлено фоновое задание. Чтобы разрешить прерывание, установите переменную notify (12.06). Выключить описанный механизм и разрешить фоновому процессу выводить данные в любое время позволяет следующая команда: % stty -tostop Дополнительные сведения о команде stty вы найдете в параграфе 41.02. - JP 12.08 Управление заданиями: резюме Если не указано иное, следующие команды действительны только в С shell, Kom shell и bash. • Команда & (U6, i2.oi). Выполняет указанную команду в фоновом режиме. Можно продолжить выполнение в интерактивном режиме. Действует во всех интерпретаторах shell. • [CTRL-cl (38.09). Уничтожает текущий интерактивный процесс, посылая сигнал INTR (38.08). Действует во всех интерпретаторах shell. • [CTRL-z] (12.01,12.04). Приостанавливает текущий интерактивный процесс, посылая сигнал TSTP (38.08). * Эта команда инициализирует драйвер терминала (42.oi) для всех запущенных с него процессов. Для порожденных интерпретаторов shell (2.02) этот драйвер инициализировать не нужно. Управление заданиями 191
12.09 • suspend (22.22). Приостанавливает интерпретатор shell. • stop или идентичная пользовательская команда ог.оз). Приостанавливает фоновое задание. • bg %л (12.01). Позволяет продолжить выполнение остановленного задания с номером л в фоновом режиме. • fg %n (12.01). Переводит фоновое или остановленное задание с номером л в интерактивный режим. • kill %л (12.01). Уничтожает фоновое задание с номером п. • kill идентификатор процесса (38.W). Уничтожает любое задание, имеющее указанный идентификатор процесса. Действует во всех интерпретаторах shell. • jobs (12.01). Выводит список фоновых и остановленных заданий, а также их номера. • set notify (I2M). Устанавливает режим немедленного вывода сообщений об изменении состояния процессов. • stty tostop (12.07). Устанавливает режим останова фонового процесса при его попытке выводить данные на экран. - ML 12.09 Запуск нескольких экземпляров shell Интересной альтернативой средствам управления заданиями (n.oi) является профамма screen, которая позволяет запускать на одном терминале несколько экземпляров shell с возможностью произвольного перехода между ними. $сгееп Рассмотрим следующую ситуацию. После входа в систему мне нужно ответить на почтовое ■сообщение гт.зз), "но сначала я хотел бы кое-что проверить. Обычно для этого мне нужно либо выйти из профаммы mail, либо осуществить временный выход в shell (зо.гб). А вот при использовании профаммы screen я могу просто переходить из одного интерпретатора shell в другой. В одном интерпретаторе я мог бы запустить профамму mail, в другом — выполнять проверку, в третьем — редактировать файлы и т.д. Допускается применение до 10 экземпляров shell. (В системе Linux можно выбирать одну из восьми виртуальных консолей, нажимая клавишу [ALT] и одну из клавиш от [F1] до [F8]. Однако это не может сравниться с прекрасными возможностями профаммы screen.) При запуске профаммы screen появляется разделитель экрана, который можно отключить, изменив файл ШОМЕ/.screenrc. После нажатия клавиши [SPACE] или [RETURN] вы попадете в исходный, интерпретатор shell с обычным системным приглашением. Это экран 0. Экран 0 Этот интерпретатор shell можно использовать для чтения почты. lmui@ruby 2 6% mail Mail version SMI 4.0 Wed Oct 23 10:38:28-PDT 1991 Type ? for help. "/usr/spool/mail/lmui": 42 messages 6 new N 38 kramer Wed Oct 28:10:31 20/654 Posting on сотр.unix.que N 39 tim Wed Oct_28:10:46 39/1485 Re: awf N 40 tim Wed Oct 28:10:47 26/832 Re: announcement of vol8 & Предположим, я прочитал почтовое сообщение, в котором интересуются моим мнением о заметке в фуппе новостей. Чтобы прочитать заметку, прежде чем отвечать, я могу не выходить из почтовой профаммы, а нажать клавиши [CTRL-a][CTRL-c] и запустить другой shell, а в нем — профамму чтения новостей. Это новое окно имеет номер 1. 192 Часть вторая. Поручите грязную работу компыотерУЦ
Экран 1 12.09 lmui@ruby 26% rn сотр.Unix.questions Unread news in сотр.unix.questions 333 articles ******** 333 articles in сотр.unix.questions—read now? [ynq] После прочтения соответствующей заметки я переключаюсь в исходный интерпретатор shell при помощи клавиш [CTRL-a][CTRL-a] и отвечаю на почтовое сообщение. Экран О > 38 kramer Wed Oct 28:10:31 20/654 Posting on сотр.unix.que N 39 tim Wed Oct 28:10:46 39/1485 Re: awf N 40 tim Wed Oct 28:10:47 26/832 Re: announcement of vol8 & r To: kramer@ora.com Subject: Re: Posting on сотр.unix.questions He's right that you can use -i for interactive prompting, but I don't think -f disables interactive mode. Теперь, допустим, я хочу проверить излагаемые факты, прежде чем продолжать ответ. Мне не придется закрывать свое сообщение или запускать порожденный интерпретатор shell. Я могу открыть еще одно окно shell, повторно нажав клавиши [CTRL-a][CTRL-c]. После этого можно вводить команды, которые следует проверить. Получив необходимую информацию, я вернусь в предьшущий интерпретатор shell, нажав [CTRL-a][0], чтобы явно вызвать нулевой экран. Интересной особенностью программы screen является возможность "замораживания" экрана, позволяющая выйти из системы и войти в систему с другого компьютера, сохранив состояние экрана (3.07). - LM Управление заданиями 7 9-171 293
13 Переадресация ввода-вывода 13.01 Использование стандартного ввода-вывода Нет никакой разницы между чтением данных из файла и с терминала.* Точно так же, если выходные данные программы состоят исключительно из алфавитно-цифровых символов и знаков препинания, нет никакой разницы между записью в файл, выводом на терминал и передачей данных на стандартный ввод другой профаммы (например, в канал). Стандартное устройство ввода-вывода предоставляет нескольких простейших средств обслуживания ввода-вывода. Таковыми являются три стандартных потока: ввода, вывода и ошибок. В соответствии с соглашением стандартный поток вывода (stdouf) содержит все "нормальные" выходные данные профаммы, в то время как стандартный поток ошибок (stderr) состоит из сообщений об ошибках. Часто бывает удобно обрабатывать стандартный вывод отдельно от сообщений об ошибках. По умолчанию стандартным устройством ввода профаммы является клавиатура, а стандартные потоки вывода и ошибок направляются на экран терминала. Стандартный поток ввода (stdin) обычно поступает с клавиатуры. Многие программы не используют stdin, и входные файлы указываются непосредственно в командной строке. Например, команда cat file 7_/?/е2 читает не стандартный поток ввода, а непосредственно файлы file] и file2. Но если в командной строке не указаны имена файлов, то, как правило, команды UNIX, требующие ввода, читают stdin. Обычно стандартный поток ввода поступает с клавиатуры, но shell может переадресовать ввод данных из другого файла. Это удобно для тех команд UNIX, которые не могут напрямую открывать файлы, например для команды mail (из). Чтобы отправить файл по почте пользователю joan, используется оператор < имя_файла, сообщающий интерпретатору shell, что к стандартному входу профаммы mail вместо клавиатуры необходимо подключить файл: % mail joan < myfile Основным достоинством стандартного ввода-вывода является возможность переадресации ввода или вывода с терминала в файл. Как мы уже говорили, UNIX является файлово-ори- ентированной системой (t.29). Поскольку терминалы и другие устройства ввода-вывода рассматриваются как файлы, то программы даже не знают** о том, куда они посылают свои выходные данные — на терминал или в файл. Если, например, вы хотите выполнить команду cat file] file2 и направить выходные данные в файл file3, а не выводить их на терминал, введите следующую команду: % cat file2 file2 > file3 Это называется переадресацией стандартного вывода в файл file3. Если после выполнения этой команды просмотреть файл file3, в нем окажется содержимое файлов file] и file2, расположенное последовательно, — точно так же, как это выглядело бы на экране, если не указать аргумент > file3. Если входные данные программы состоят исключительно из алфавитно-цифровых символов и знаков препинания (т.е. ASCII-символов или международных (не американских) символов). Программа может это определить. 194 Часть вторая. Поручите грязную работу компьютер]/
13.01 Наиболее распространенной формой переадресации в UNIX является канал, который обозначается оператором | (вертикальная черта). Чтобы, например, направить корреспонденту Joan файлы filel и file2 в одном почтовом сообщении, введите следующее: % cat file2 £ile2 | mail Joan Оператор канала означает следующее: "Подключить стандартный вывод процесса слева (cat) к стандартному вводу процесса справа (mail)". В парафафе 45.20 приведены рисунки и дополнительная информация относительно стандартного ввода-вывода и переадресации. В табл. 13.1 перечислены наиболее популярные способы переадресации ввода-вывода как для С shell, так и для Bourne shell. Таблица 13.1. Обычные способы переадресации стандартного ввода-вывода Действие Переадресовать stdout в файл Переадресовать stderr в файл Переадресовать stdout и stderr в файл Чтение stdin из файла Переадресовать stdout в конец файла ■ Переадресовать stderr в конец файла Переадресовать stdout и stderr в конец файла Чтение stdin с клавиатуры, пока не будет введен символ с (см. парафаф 8.18) Направить stdout в канал Направить stdout и stderr в канал csh программа программа программа программа программа программа программа программа > файл >& файл < файл » файл >>& файл «с I программа 2 & программа 2 sh программа программа программа программа программа программа программа программа программа программа > файл 2> файл > файл 2>&1 < файл » файл 2» файл >> файл 2>&1 <<с I программа2 2>&1 программа2 Следует учитывать следующее: • Несмотря на то, что стандартный ввод-вывод является основным элементом UNIX, синтаксис его переадресации зависит от типа используемого интерпретатора shell. Синтаксис команд переадресации в Bourne shell и С shell отличается, особенно если используются не очень популярные способы переадресации. Синтаксис в Korn shell и bash незначительно отличается от синтаксиса в Bourne shell. • Переадресацию стандартного ввода и вывода можно выполнить в одной командной строке. Например, для выполнения чтения из файла input и записи в файл output выполните следующую команду. % программа < input > output Bourne shell предоставляет дополнительную возможность: с его помощью вы можете переадресовать stderr в третий файл: $ программа < input > output 2> errors • С shell не обеспечивает простого способа переадресации стандартного потока вывода без переадресации стандартного потока ошибок. Один простой прием позволит вам исправить этот недочет. Чтобы поместить стандартные потоки вывода и ошибок в разные файлы, выполните команду, подобную следующей: % (программа > output) >& errors Схожие команды описаны в парафафах 13.03 и 13.05. Пвреадресация ввода-вывода 195 7»
13.02 • Для многих реализаций shell обоих типов безразлично, в каком порядке следуют операторы переадресации и в каком месте командной строки они находятся. Например, SunOS позволяет вводить команду < input > output программа. Такие команды не соответствуют принятым в UNIX нормам относительно построения команд. Проще понять, что происходит, если сначала ввести имя команды, а затем по очереди переадресовать стандартные потоки ввода, вывода и ошибок. Существуют более сложные способы переадресации ввода-вывода, главным образом — в Bourne shell (45.21, 45.22, 45.23). Конечно, профаммы работают не только со стандартными потоками ввода-вывода. Они могут также открывать другие файлы, определять свои каналы специального назначения, а также выводить данные непосредственно на терминал. Но стандартный ввод-вывод является тем связующим элементом, который позволяет из маленьких программ формировать большие. Следовательно, он является важнейшим элементом операционной системы. Большинство утилит UNIX читают данные со стандартного ввода и записывают в стандартный вывод, что позволяет легко комбинировать их. Профамма, создающая собственный канал специального назначения, может оказаться очень полезной, однако ее нельзя использовать в комбинации со стандартными утилитами. Некоторые системы UNIX и такие утилиты, как gawk рз.12), используют стандартные имена файлов, в частности /dev/stdin, /dev/stdout и /dev/stdeir. Эти файлы можно применять наравне с любыми другими файлами. Например, чтобы заставить обычную команду читать сначала файл aflle, затем стандартный ввод (например, с клавиатуры), а в конце файл bfile, введите следующее: % команда afile /dev/stdin bfile Таким же образом процесс может выводить свои данные в файл /dev/stdout, а сообщения об ошибках — в файл /dev/stderr. - ML, JP 13.02 Команде cat недостаточно одного аргумента Что неправильно в следующей командной строке? cat 25.02 % cat файл1 | tr -d '\015' > файл2 Том Кристиансен писал в своей заметке в Usenet: Один мудрец сказал: "Если вы поймаете себя на том, что вызываете команду cat только с одним аргументом, знайте: вы делаете что-то не то". В этой командной строке команда cat используется только для того, чтобы подключить файл к стандартному вводу команды tr. Эффективнее заставить shell самостоятельно выполнять переадресацию при помощи оператора < (H.oiy. % tr -d '\°15' < файл1 > файл2 - JP, ТС 13.03 Направление в канал только стандартного потока ошибок Оператор | в командной строке перенаправляет стандартный поток вывода одного процесса другому. Как перенаправить стандартный поток ошибок? Например, вам нужно запустить в фоновом режиме команду cruncher, выполнение которой занимает продолжительное время, сохранить ее вывод в файле и отправить себе по почте копии сообщений об ошибках. Выполним команду в порожденном интерпретаторе С shell оз.от). Переадресация стандартного вывода команды производится в порожденном интерпретаторе. Вне его пределов остается только стандартный поток ошибок, который (вместе с пустым стандартным потоком вывода) перенаправляется профамме mail ц.зз) оператором I & рз.05): % (cruncher > файл_вивода) |S mail ааше_амв £ [1] 12345 ~ Щ Часть вторая. Поручите грязную работу компьютеру
13.04 Конечно, это задание можно выполнять в фоновом режиме qm). Если вы хотите, чтобы стандартный поток вывода поступал на терминал, а не в текстовый файл, используйте в качестве файла вывода /dev/tty (45.20). Bourne shell предусматривает намного больше способов переадресации, благодаря которым вы можете сделать именно то, что вам нужно. Недостатком является только усложненный синтаксис (45.21). Вот как можно запустить программу cruncher, направить по каналу файл stderr программе mail и вывести на экран файл stdout $ (cruncher 3>fil 1>в2 2>£3 3>6-) | mail влшв_имя s 12345 Чтобы stdout переадресовать в файл вывода, a stderr направить в канал, попробуйте выполнить следующую команду: $ (cruncher 3>fil >фаял_вивада 2>fi3 3>fi-) | mail яашв_ама fi 12345 - JP 13.04 Проблемы при пересылке данных программам постраничного вывода Если окно в системе UNIX (терминал, X Window, коммуникационная программа) не может вместить всю предыдущую информацию, такие программы постраничного вывода, как more (25.03), pg и fess (25.04), окажутся исключительно удобными. Однако передача данных по каналу не всегда производится должным образом. В следующей строке команда grep выполняет поиск в нескольких файлах. Что в ней неправильно? % grep "Aset" */•cshrc | more Это непростой вопрос. Как оказалось, проблема в том, что файлы barney/.cshrc, edie/.cshrc и gail/.cshrc защищены от чтения (22.02). Но, как указано в верхней части рис. 13,1, сообщение об ошибке вытесняется за пределы экрана, а программа постраничного вывода не предотвращает этого. Если только ваш дисплей работает не очень медленно, сообщения об ошибках теряются, и вы даже не осознаете, что они были. Эти сообщения могут также проникнуть в. стандартный вывод команды grep. Причина в том, что вы указали интерпретатору shell направлять программе постраничного вывода только стандартный поток вывода команды grep. Сообщения об ошибках программа grep записывает в стандартный поток ошибок (45.20}. Однако оба потока — и stdout, и stderr— одновременно выводятся на экран. Сообщения об ошибках из stderr прокручиваются за пределы экрана вместе с выходными данными программы постраничного вывода. Эта программа не учитывает строки сообщений об ошибках, поэтому она весь экран заполняет данными из stdout ("нормальные" данные). Если выходные данные программы grep (извлеченные из прочитанных ею файлов) занимают не меньше.экрана, как в данном примере, полученное множество строк не помещается на экране, вследствие чего часть строк уходит за его пределы. Для решения этой задачи лучше объединить файлы stdout и stderr программы grep и передать их программе постраничного вывода. Это можно сделать с помощью одной из следующих строк (для csh и sh): % grep "Aset" */.cshrc |£ more $ grep "Aset" */.cshrc 2>6l 1 more В нижней части рисунка 13.1 показано, как это работает. При передаче данных программам постраничного вывода, я всегда таким образом объединяю потоки stdout и stderr. - JP Пореадресация ввода-вывода 197
13.05 Текст из стандартного потока ошибок, выделен полужирным шрифтом. % gx«p "*e»t" V-cahxc I nor* Компьютер 9rep }111щ more ^йр1яДхзз1 не может открыть.. ШМШШвМШИ.ЯИИ % grap ""e»t" */.cehro |С nor* т adrian/.cshrcrset prompt="adrian \!% adrian/.cahrc:set history - 24 bob/.cshrc:setenv TZ EST5EDT bob/,cahrc:aet history=100 bob/.cshrcrsetenv USER bob fran/.cahrcrset hiatory=5Q fran/.cahrc:set host-'hostname1 fran/.cehrc:set filec —More—gxap: can't opan gail/.cahrc &&&&F*4 adrian/.cshrcrset history=50 adrian/.cshrc:set host-'hostname' adrian/.cshrcrset prompt«="adrian \!% adrian/.cshrcrset history » 24 grap: can't opan barnay/.cahrc bob/.cshrcrsetenv TZ EST5EDT bob/.cshrc:set history=100 bob/.cshrc:setenv USER bob —More— Рис. 13.1. Стандартный поток ошибок, проходящий мимо канала и через канал 13.05 Переадресация в С shell: сообщения об ошибках такс 2S. 13 Оператор > перенаправляет стандартный вывод процесса в файл. Он не влияет на стандартный поток ошибок. Если вы работаете в системе и видите все сообщения, записываемые в стандартный поток ошибок, все нормально: % nroff -ms report.ms > report.out fi [1] 10316 ...другие команды... nroff: can't open file /hoem/jpeek/report.data Но если вы выйдете из системы, оставив работающее задание, то не увидите сообщения об ошибках, если только не будете использовать оператор >&. Он перенаправляет в файл и стандартный вывод, и сообщения об ошибках. Например: % make >& так*.output & [1] 10329 % logout ...другие команды. . . % cat make.output ее -о -с random.с ее -О -с output.с "output.с", line 46: syntax error "output.с", line 50: time_e undefined "output.c", line 50: syntax error 198 Часть вторая. Поручите грязную работу компьютеру
13.06 Пока вы работали в системе, можно было воспользоваться оператором >& и просмотреть выходной файл с помощью команды tail -/(25.щ. Если вы не хотите смешивать сообщения об ошибках с другими выходными данными, направьте их в разные файлы (см. параграф 13.01). В С shell есть также оператор канала | &, перенаправляющий и стандартный вывод, и сообщения об ошибках. Он чрезвычайно удобен при выполнении заданий в фоновом режиме или на другом компьютере, а также при пересылке выходных данных с помощью программы mail (1.33): % make |& mailx -s "'make bigprog' output" jpeek@jpeek.com & [1] 29182 29183 Если бы я вместо оператора | & использовал оператор I (вертикальная черта), сообщения об ошибках не попадали бы в почтовое сообщение. - JP 13.06 Безопасная переацресация ввода-вывода Приходилось ли вам случайно испортить файл? Установив переменную noclobber в С shell или флаг noclobber в bash или ksh, вы избежите подобных ошибок. Установка переменной noclobber предотвращает случайное разрушение данных файла при переадресации стандартного вывода (13.01). Рассмотрим следующую ситуацию: % команда > ниходно4г_файл В этой строке команда удаляет старый выходной файл и создает новый. Если вы либо неправильно ввели имя файла, либо забыли, что такой файл уже существует и содержит важные данные, либо (что вероятнее всего) собирались набрать >> вместо > (т.е. на самом деле намеревались дописывать данные в конец выходного файла, а не создавать новый), то, к сожалению, ваши старые данные будут уничтожены. Установка переменной noclobber предотвращает подобные проблемы. Если эта переменная существует, С shell не позволяет разрушить имеющийся файл, если только явно не указано его удаление с помощью восклицательного знака после оператора переадресации (или вертикальной черты в ksh и bash). Приведем примеры. Левый столбец характерен для csh и tcsh, а правый — для bash и ksh: % set noclobber r $ set -о noclobber % Is $ Is filea fileb filea fileb % программа > fileb $ программа > fileb fileb: File exists. bash: fileb: Cannot clobber existing file % программа >! fileb $ программа >| fileb He забудьте поставить пробел после восклицательного знака, иначе С shell решит, что вы делаете ссылку на перечень ранее введенных команд и (как правило) выведет сообщение об ошибке следующего типа: fileb: Event not found. Помните: noclobber не является переменной среды, поэтому каждый новый экземпляр shell не будет наследовать ее (б.щ. Следовательно, чтобы режим noclobber всегда был установлен, вставьте приведенную в примерах команду set в файл конфигурации (2.92) своего интерпретатора shell. Примечание: В некоторых версиях С shell переменная noclobber предотвращает переадресацию стандартного вывода в устройство /dev/null (1з.Ы) или на терминал, если не добавить символ !. Переадресация ввода-вывода 199
Переменная noclobber в С shell выполняет еще одну роль, о которой стоит упомянуть. Обычно С shell позволяет дописывать данные в несуществующий файл. Если в csh или tcsh эта переменная установлена, то дописывать данные можно только в существующий файл, если не используется восклицательный знак: % is filea fileb % программа » fileс filec: No such file or directory % программа »! filec % - ML, JP 13.07 Порожденный shell: оператор () При работе с интерпретатором shell скобки используются () для группирования команд. Группирование нескольких команд Вывод целой группы команд может быть целиком направлен в один канал. Например: echo S.Об $ (cat filel; echo .bp; cat file2) | nroff Эта командная строка позволяет поставить между именами двух файлов, подлежащих форматированию, оператор конца страницы .Ьр программы nroff* (4з.и). Скобки удобны также для выполнения в фоновом режиме Bourne shell целой последовательности команд, разделенных точками с запятой. В С shell приведенная ниже команда сразу выполняется в фоновом режиме: & 1.26 % nroff -ms filel; nroff -ms file2 & Но в Boume shell оператор фонового режима & воздействует только на вторую команду, что заставит вас ждать, когда же завершится первое задание, чтобы появилось системное приглашение. Чтобы сразу можно было продолжить работу, введите следующее: $ (nroff -тз filel; nroff -ms file2) S Временный переход в другой катапог и изменение среды Оператор () указывает, что нужно запустить порожденный shell (З8.04). При выполнении команд, заключенных в скобки, не затрагивается среда родительского интерпретатора shell. Чтобы, например, запустить команду в другом каталоге, не меняя текущий каталог активного интерпретатора shell (зв.оз), выполните следующие команды: % pwd /home/trent % (cd другой_каталог; nroff -ms filel > file.out) S [1] 22670 % pwd /home/trent Файл file.out будет создан в другом_каталоге. В параграфе 13.8 описан еще один метод группировки команд в Bourne shell. Его возможности более ограниченны, но он может быть довольно эффективным. - TOR, JP . * Если вы используете только команду cat и единственную команду echo, вместо приведенной выше можно выполнить следующую команду: $ echo .Ьр | cat filel - file2 | nroff Опция - Команды cut (шз) заставляет команду cat читать в данном месте свой стандартный ввод (в данном- случае — из канала и команды echo). Команда nroff в обоих случаях получает одинаковые входные данные.. . .L~ 200 Часть вторая. Поручите грязную работу компьютеру:
13.09 13.08 Использование фигурных скобок для группирования команд в Bourne shell Известно, что выходные данные последовательности команд можно объединять при помощи порожденных интерпретаторов shell оз.т). Это значит, что вместо последовательности команд $ date > log $ who » log $ Is » log посредством скобок запускается порожденный интерпретатор shell: $ (date > 9.13 > who > Is) > log И только однажды выходные данные переадресовываются в файл log. Однако порожденный интерпретатор shell является дополнительным процессом, и на его запуск в загруженной системе тратится какое-то время. Если все, что нужно сделать, — переадресовать вывод (или ввод) набора команд, используйте оператор списка {}. $ { date > who > Is > } > log Обратите внимание на пробелы и дополнительный символ возврата каретки в конце списка. Каждая команда в списке должна быть отделена от других. Можно ввести следующее: $ { date; who; Is; } > log Заметьте: после каждой команды стоит точка с запятой. Между операторами запуска порожденного интерпретатора shell — () — и операторами списка — {} — есть следующие различия: в В порожденном интерпретаторе shell команда cd не изменяет текущий каталог родительского интерпретатора, а оператор списка — изменяет. в Набор переменных порожденного интерпретатора shell не передается родительскому интерпретатору, а переменные из списка — передаются. Примечание: ,-'■'■■■"-- Джонатан Кейменс (Jonathan I. Kamens) обнаружил, что некоторые версии Bourne shell выполняют список команд в порожденном интерпретаторе shell, особенно если задействованы каналы. Если ваша версия Bourne shell работает так, как в следующем примере, он также использует порожденный интерпретатор shell. $ { echo frep; foo=bar; } | cat frep $ echo $foo $ { echo frep; foo=bar; } frep $ echo $£oo >'. . .bar - JP 13.09 Направление потока вывода по разным адресам ВЕсли при выполнении команды выходные данные направляются в файл, но вы хотите видеть их и на экране, чтобы в случае неполадок остановить программу, воспользуйтесь программой tee. Эта программа читает свой стандартный ввод и записывает его в один или несколько *» файлов. (На компакт-диске есть GNU-версия этой программы.) И*реадресация авода-вывода 201
13.10 Примечание: Канал может буферизовать выходные данные программы, собирая их в блоки и время от времени передавая дальше. Если выходные данные поступают медленно и передаются программе tee по каналу, возможны задержки, прежде чем вы увидите их. В таком случае лучше воспользоваться оператором > и переадресовать вывод в файл, выполнить программу в фоновом режиме и проанализировать данные с помощью программы tail -f (25.16). Можно также воспользоваться такой программой, как script (Si.osj. Используйте программу tee для сохранения результатов посредине длинного конвейера команд. Это особенно удобно при отладке. Например, можно ввести такую последовательность команд % программа | tee программа.out | sed -f sedscr | tee sed.out | ... чтобы сохранить выходные данные программы в файле программа.ош, а затем передать их программе sed, сохранить выходные данные программы sed в файле sed.out и передать их дальше... Дописывать данные в существующий файл позволяет опция -а: * ...tee -а ямя_файла... - JP 13.10 Как направить выходные данные нескольких команд в один файл Программа fee (U.w) записывает выходные данные в файл и одновременно направляет их в стандартный поток вывода. Может понадобиться собрать выходные данные нескольких команд и по очереди направить их в один и тот же файл с помощью команды tee. Очевидный способ решения этой задачи — применение опции -а: $ команда! | tee файл $ команда? | tee -а файл $ команда3 | tee -а файл Более простой способ решения заключается в следующем: $ (команда! > 9. IS > команда2 > команда.?) | tee файл С помощью операторов () (is.07j объединяют выходные данные трех команд. Затем эти данные передаются одной команде — tee. Результат обработки тот же, но во втором случае используется на два канала и две программы tee меньше и на один порожденный интерпретатор shell больше. К сожалению, в С shell все не так просто. Если все поместится в одной строке, она должна выглядеть следующим образом (то же относится и к Bourne shell): % (команда!: команда?; команда.?) I tee файл Иначе нужно ставить в конце строк точку с запятой и обратную косую черту: % (команда!; \ команда?; \ команда^) | tee файл — JP 13.11 Направление стандартного вывода по различным адресам: программа tpipe Программа tpipe похожа на команду fee (им), но она не помещает копию своего стандартного ввода в файл, а передает ее в другой канал. Можно эмулировать программу tpipe при помоши команды tee и последующего выполнения других команд с fee-файлом, однако бывают случаи, когда нежелательно засорять свой диск временными файлами. ' 202 Часть вторая. Поручите грязную работу компьютф/,.
13.12 Предположим, у меня есть несколько сжатых PostScript-файлов большого объема. Мне нужно распечатать эти файлы, и при этом я хочу знать, сколько в них страниц. Мне известно, что количество страниц указывается в строке, следующей в конце файла после %%Pages:. Посредством программы gzcat (24.07), предназначенной для распаковки файлов и направления их в стандартный вывод, я могу ввести следующие команды в цикле fbr (9.12) (или поместить их в сценарий). В этом цикле каждый файл направляется на принтер, а программа sed выбирает нужную строку: for f do gzcat $f I lpr gzcat $f I sed -n "s/A%%Pages: \ ( [0-9] [0-9]*\)/$f: \1 pages/p" done Однако при этом команда gzcat вызывается дважды, для чего, разумеется, требуется больше времени, чем обычно. Я мог бы сначала распаковать файл при помощи программы gunzip, но не уверен, что для этого хватит дискового пространства. Используя профамму tpipe, можно все это сделать в одной строке, не запуская новых процессов и не занимая много дискового пространства: for f do gzcat $f I tpipe lpr I sed -n "s/*%%Pages: \([0-9] [0-9]*\)/$f: \1 pages/p" done При выполнении этого сценария и после направления каждого файла на принтер на экране появляются следующие сообщения: ch01.ps.gz: 44 pages ch02.ps.gz: 51 pages ch03.ps.gz: 33 pages Если у вас нет профаммы tpipe, сымитируйте ее с помощью профаммы awk (зз.пу. gzcat $f I awk "{ print I V'lprV ; print ]" I \ sed -n "s/A%%Pages: \([0-9] [0-9]*\)/$f: \1 pages/p" Этот вариант команды работает намного медленнее, но задание выполняет. - LM 13.12 Отображение текста на нескольких терминалах Чтобы на нескольких терминалах отображались данные, вводимые одним пользователем, необходимо выполнить следующие операции: 1. Человек, проводящий конференцию, запускает интерактивный интерпретатор shell, например, при помощи одной из следующих команд: % csh -i IS tee /tmp/log $ csh -i 2>Sl tee /tmp/log Выйти из процесса можно с помощью команды exit. Чтобы запустить Bourne shell, вместо csh наберите sh. 2. Каждый, кто хочет участвовать в конференции, вводит tail-f25.M % tail -f /tmp/log и использует команду [CTRL-c], чтобы уничтожить процесс tail -/по окончании сеанса. В данном случае недостатки могут быть следующими, в Человек, проводящий конференцию, не может использовать такие полноэкранные профаммы, как vi, которые предполагают вывод на экран, а не в канал. Олрвадресация ввода-вывода 203
в Команды могут выводить текст только на экран, но не в файл log. Если такое случается, то ведущий конференции должен ввести команду csh -iv для запуска С shell или команду sh -iv для запуска Bourne shell. - JP Дефис вместо имени файла Если ввести имена файлов в командной строке, эти файлы будет читать типичная команда UNIX. Если имена файлов не указаны, команда будет читать стандартный ввод. Как заставить команду читать и файл, и стандартный ввод? Некоторые системы UNIX и такие утилиты, как gawk (33.12), используют специальное имя типа /dev/stdin (13.01). Более старые команды UNIX, такие как cat (25.02) и difi[(28.oi), воспринимают такое псевдоимя файла, как дефис. На самом деле файла с таким именем не существует. Это просто сокращение для операции чтения стандартного ввода. Примечание: В будущем этот синтаксис может измениться. Вот как, например, можно сравнить два файла на разных компьютерах. Команда rsh snooze cat bin/aprog с удаленного сервера snooze направляет по каналу копию файла bin/aprog на локальный компьютер. Команда ^сравнивает локальный файл aprog.new со стандартным потоком ввода из канала: rsh 1.33 % rsh snooze cat bin/aprog I diff - aprog.new Дополнительные примеры вы найдете в параграфах 9.11 и 13.07. - JP 13.14 Использование файла /dev/null Устройство /dev/null не является обычным файлом, хотя его можно использовать как таковой. Это специальное устройство* в системе UNIX (не физическое), которое "поглощает" любой записываемый в него текст. При чтении текста из устройства оно возвращает символ конца файла (файл нулевой длины). Для чего же может понадобиться данное устройство? в Для очистки другого файла. Просто скопируйте файл /dev/null поверх другого файла (24.01). в Для обеспечения скрытой работы программы путем переадресации ее вывода в ато устройство. Например, вы запускаете программу в фоновом режиме и не желаете, чтобы она вас беспокоила: % программа > /dev/null При этом стандартный вывод программы перенаправляется (/3.oi), но в случае возникновения ошибок сообщения о них будут поступать на терминал, в Для ответа программам, задающим много вопросов, когда известно, что в ответ на каждый вопрос нужно просто нажать [RETURN]. Во многих случаях можно переадресовать стандартный ввод программы с устройства /dev/null: % программа < /dev/null Want the default setup? If yes, press RETURN: Enter filename or press RETURN for default: Этот прием сначала нужно применить к данной программе, чтобы убедиться, что он работает. (Если не получится, попробуйте программу yes (23.04).) * Точнее, файл устройства. 2Ш Насть вторая. Поручите грязную работу компьютеру 13.13 13.13
13.15 • Когда .программе нужен еще один файл, а вы не хотите читать или записывать настоящий файл. Например, программа grep (27.00 не будет выводить имя файла, в котором она обнаружила совпадение, если в командной строке нет хотя бы двух имен файлов. При использовании метасимволов в каталоге, где может быть только один подходящий файл, введите также /dev/null, чтобы программа grep всегда просматривала более одного файла (17.20)'. % grep "строка" * /dev/null Программа grep точно не найдет шаблон в файле /dev/null. :-) • В параграфе 24.02 приведены дополнительные примеры использования устройства /dev/null. Другим интересным устройством (используемым главным образом программистами) является /dev/zero. При его чтении возвращается бесконечная строка из NUL-символов (Si.oj). Символы новой строки отсутствуют. По этим двум причинам при чтении из этого устройства у многих программ UNIX возникают проблемы. Если хотите посмотреть, что будет прочитано из этого устройства, введите следующую команду. Команда fold организует чтение, а команда head (25.2») обеспечит остановку*: ,,-, Ш*ЗМ ■-% fold -20 /dev/zero | od -с | head OU2S.07 - JP 13.15 Что делать с переполненной битодробилкой?:-) [Описанные в этом параграфе приемы нужно применять осторожно и поручать их выполнение только высококвалифицированным системным администраторам. — JP] Вопрос. Наша система Sun SPARCstation 1+ 4.1 OW2 начала работать очень медленно. После выхода из системы я получил сообщение /dev/null full: empty bit bucket. (Устройство /dev/null заполнено. Очистите битодробилку.) В чем дело? Ответ. Проблема в том, что устройство null переполнено. Приводим основные методы очистки засоренной битодробилки. • Откройте компьютер. Отыщите битодробилку, затем найдите красную пробку снизу, выньте ее и высыпьте все в большую мусорную корзину. • Снимите заглушку Ethernet. Введите команду % cat /dev/null > 1е0 Все биты исчезнут без следа, е Если записывать в устройство /dev/null нули, они не займут места, но единицы занимают его. Попробуйте записать в /dev/null файл, содержащий одни нули. Используйте двоичные нули, а не символы ASCII 0, которые снова переполнят файл. • Эта проблема возникает, когда вы работаете на компьютере. Если его выключить, многие проблемы решатся сами собой. Скажите об этом всем пользователям. • Запустите побольше программ на С. В них есть строки, завершающиеся нулем, которые нейтрализуют все лишние биты в /dev/null. • Подумайте об увеличении емкости битодробилки, что дало бы возможность перемалывать в ней не только биты, но и байты, а также слова. • Отнесите компьютер хорошему мастеру. Он прочистит битодробилку, заменит масло и добавит присадки. Все это займет не более 29 минут. — XX (Хотели бы мы узнать, кто это написал!) * В некоторых версиях UNIX программа liead не завершается после вывода первых десяти строк. В таком случае вместо этой программы используйте sed 10q. Пфеадресация ввода-вывода 205
13.16 13.16 Хранение и просмотр сообщений об ошибках ^Г'^Ш Простой сценарий logerrs, написанный Маартеном Литмаатом (Maarten Litmaath), выполняет Г Ф 1 команду, записывает все сообщения об ошибках в файл, а также направляет их в стандартный ^ -4 поток ошибок. Поскольку стандартный поток ошибок обычно выводится на экран, это logerrs позволяет просматривать сообщения об ошибках и записывать их в файл одновременно. Первым аргументом сценария является имя файла регистрации. Далее вводится команда и все ее аргументы. Приводим пример выполнения команды cat foo barn регистрации ошибок в файле errors. Файл foo существует, а файл bar — нет. $ cat foo hello world $ logerrs errors cat foo bar hello world bar: No such file or directory $ cat errors bar: No such file or directory Всю работу выполняют следующие две строки сценария: exec 3>&1 "$@" 2>&1 >&3 | tee -a "$ERRLOG" >&2 Если операторы >& вам не знакомы и вы интересуетесь, как Bourne shell использует дескрипторы файлов, обратитесь к параграфам 45.20 и 45.21. - MAL, JP
Часть третья Работа с файловой системой Что же такое файловая система? Набор структур данных, сообщающих операционной системе, каким образом физические секторы диска организованы в файлы? Организационные принципы, делающие возможным хранение данных в упорядоченном виде, благодаря чему информация становится доступной для многих пользователей? Эффективное средство борьбы с беспорядком, основой которого является иерархическая структура, способная разрастаться, усекаться и делиться? В следующих одиннадцати главах освещаются вопросы организации файловой системы и манипулирования файлами и каталогами, столь важные для овладения искусством работы в системе UNIX. В частности будут рассмотрены следующие темы: в перемещение по файловой системе; в использование метасимволов при необходимости сослаться на несколько файлов; • поиск файлов, хранящихся в файловой системе, а также использование команды Is во всех ее формах; в применение команды find — мощного инструмента поиска файлов; • объединение, переименование и копирование файлов; • создание архивов для хранения и перемещения большого количества файлов; • резервные копии: каким образом и когда их целесообразнее создавать; в разнообразные приемы работы с файлами; в принципы организации системы владения файлами; • удаление файлов; • различные способы освобождения дискового пространства. - TOR
14 Перемещение по файловой системе 14.01 Основные способы перемещения по файловой системе Как можно быстро перемещаться по файловой системе UNIX? Можете ли вы найти любой файл или каталог файловой системы по полному либо относительному путевому имени? Каковы достоинства и недостатки символических ссылок? Многие пользователи UNIX не отдают себе отчета в том, насколько большим подспорьем в работе могло бы стать глубокое понимание основных принципов построения файловой системы. Для начала мы затронем несколько основных тем и понятий, а также опишем отдельные приемы, которые обязательно следует знать: в использование относительных и полных путевых имен (параграф 14.02); в чем удобен текущий каталог (параграф 14.03); в экономия времени и сокращение объема вводимых данных при смене каталогов с помощью переменной cdpath (параграф 14.05); в возможность быстро переходить в рабочие каталоги посредством использования списка, хранящегося в стеке каталогов (параграфы 14.06, 14.07); в быстрые версии пользовательской команды cd (параграф 14.08); в использование переменных и тильды (~) для упрощения поиска каталогов и файлов (параграфы 14.10, 14.11); в пользовательская команда mark, помечающая каталоги, что позволяет быстро возвращаться в них (параграф 14.12); в проблемы, возникающие при смене каталога с помощью символических ссылок (параграф 14.13); в автоматическая инициализация при входе и выходе из каталога (параграф 14.14). - JP Н.02 Относительные и полные путевые имена Все элементы файловой системы UNIX — файлы, каталоги, устройства, именованные каналы и т.д. — имеют по два путевых имени: полное и относительное. Зная, как создаются такие имена, вы сможете определять наиболее целесообразные способы поиска файлов (или других элементов) и работы с ними. Несмотря на то, что принципы создания путевого имени чрезвычайно просты, для начинающих их освоение представляет одну из самых больших трудностей. Внимательное изучение данного парафафа позволит вам впоследствии сэкономить много времени и нервов. Рассмотрим рис. 14.1, на котором представлена структура файловой системы UNIX. Ni&f ■■ ещение по файловой системе 209
>х**»»л"чэд»: i home Jim jane > calendar SI Sep I | Oct [ (output). | Sub | fa) (b) (c); „J I * 0;O0:Ct) O0CD work I -Каталог ЩЩЩ - Текущий каталог 1щ5?У - Символическая ссылка (Т\- -Файл Рис. 14.1. Иерархическая структура файловой системы UNIX О том, как образуются полные и относительные путевые имена, можно судить по табл. 14.1. Таблица 14.1. Различия между полными и относительными путевыми именами Полное путееое имя Относительное путевое имя Первым указывается имя корневого каталога Всегда начинается с косой черты (/) Полное путевое имя определенного объекта (например, файла) никогда не изменяется Первым указывается имя текущего каталога (i.2i) Никогда не начинается с косой черты (/) Относительное путевое имя объекта зависит от имени текущего каталога Полное путевое имя создается следующим образом: • войдите в корневой каталог (/) и перемещайтесь вниз по дереву файловой системы; • после имени каждого каталога ставьте косую черту (/). Если путевое имя заканчивается на имени каталога, то косая черта после него не является обязательной. При необходимости получить, скажем, список каталога, выделенного на рис. 14.1, вне зависимости от того, какой каталог является в настоящее время текущим, нужно использовать полное путевое имя: % la /hon Sub a i/jane/data b с Создать относительное путевое имя несколько сложнее: • войдите в текущий каталог; • продвигаясь в направлении от корневого каталога, добавляйте имена подкаталогов; 210 Часть третья. Работа с файловой систем^
14.03 • продвигаясь в направлении к корневому каталогу, добавляйте по две точки (..) для каждого родительского каталога; • после каждого имени каталога ставьте косую черту. Правда, если путевое имя принадлежит каталогу, то косая черта в конце не обязательна. Например, если вашим текущим каталогом является каталог, выделенный на рис. 14.1, то для получения списка содержимого подкаталога Sub необходимо воспользоваться следующим относительным путевым именем: % is sub d e f Если вы не перешли в другой каталог, то прочитать файл d в каталоге Sub можно с помощью относительного путевого имени: % cat Sub/d Чтобы сделать текущим каталог с именем jim, можно использовать его относительное путевое имя: % cd ../../jim Правда, в последнем случае проще было бы воспользоваться полным путевым именем /home/jim. Символическая ссылка (ism) придает путевому имени новое свойство. Какие два полных путевых имени будут соответствовать файлу, на который указывает символическая ссылка? Ответ: /home/jane/.setup и /work/setups/generic. (Второе имя указывает непосредственно на файл, что обеспечивает более быстрое обращение к нему.) Если ваш текущий каталог является тем, который выделен на рис. 14.1, то как проще всего прочесть этот файл при помощи программы тоге (23.03)2 Наверное, ..посредством символической ссылки . % more ../.setup Запомните: если вам нужно выполнить какие-то действия в файловой системе, не спешите сразу менять каталог. Подумайте об использовании относительного или полного имени при вызове команды. Получив сообщение об ошибке, проверьте корректность путевого имени. Как правило, проблема заключается в нем. Если вам сложно представить себе файловую систему, помочь могут программы, создающие схему дерева каталогов (if>.i% 16.20). - JP 14.03 Чем хорош текущий каталог Пользователи, которые думают, что им при работе с текущим каталогом нужно знать только команду cd, должны обязательно прочитать этот параграф! Понимание того, как система UNIX использует текущий каталог, очень важно. Каждый процесс в UNIX имеет собственный текущий каталог. В частности, его имеет ваш интерпретатор shell. Когда shell запускает процесс, то порожденный процесс имеет тот же текущий каталог, что и родительский. Откуда, например, команда Is знает, список какого каталога нужно выводить? Она использует текущий каталог, который унаследовала от родительского процесса, т.е. от интерпретатора shell: % is ...Выводится список текущего каталога, который является также текущим каталогом интерпретатора shell Каждый процесс может сменить свой текущий каталог, и это не приведет к изменению текущих каталогов других выполняющихся процессов. Поэтому: • сценарий (который выполняется как отдельный процесс) может сменить каталог, не влияя на интерпретатор shell, который его запустил (сценарию до его выполнения не обязательно менять каталог); Перемещение по файловой системе 211
14.04 • если на экране открыто несколько окон (или вы произвели несколько сеансов регистрации в системе), то для каждого из действий выполняются отдельные процессы. Следовательно процессы имеют различные текущие каталоги; • при использовании порожденного интерпретатора shell рз.07, за.04) или при временном выходе в shell вы можете перейти в любой каталог. После завершения таких процессов текущий каталог родительского интерпретатора shell не изменится. Если вы хотите выполнить команду в другом каталоге, без предварительного перехода в него (а затем обратно), сделайте это в порожденном интерпретаторе: % pwd /foo/bar % (cd каталог; команда > файл) % pwd /foo/bar В чем преимущество перехода в новый каталог? А в том, что его относительное путевое имя начинается с имени текущего каталога. Воспользовавшись именем текущего каталога, вы можете сослаться на файл, зная его относительное путевое имя, скажем afile. Если бы понятия текущего каталога и относительного имени отсутствовали, то нужно было бы всегда использовать полное путевое имя аш), например: /usr/foe/projects/alpha/afile. - JP 14.04 Как UNIX определяет текущий каталог Многие команды, в том числе и pwd, наследуют текущие каталоги запускающих их процессов (обычно это интерпретатор shell). Команда может быть запущена из любого каталога. Как процесс pwd определяет, в каком месте файловой системы он находится? Рассмотрим рис. 14.2, на котором изображены текущий каталог /usr/joe_vi его родительские каталоги. Текущий каталог не имеет собственного имени, поэтому процесс pwd не может его здесь найти. Но текущий каталог хранит запись с именем . (точка), по которой можно определить номер индексного дескриптора каталога (шю). Номер индексного дескриптора текущего каталога равен 234. Далее процесс pwd просит систему UNIX с помощью относительного путевого имени .. открыть родительский каталог, который стоит на один уровень выше. Ищется имя, соответствующее дескриптору с номером 234. Ага, текущий каталог называется joe. Следовательно, путевое имя заканчивается на joe. Следующий шаг. Процесс pwd ищет в кахзлоге, стоящем на один уровень выше текущего, запись с именем ., чтобы определить номер ее индексного дескриптора, который равен 14. Как всегда, имя каталога, стоящего на один уровень выше, находится в его родительском каталоге (.., номер индексного дескриптора — 12). Поэтому, чтобы определить данное имя, процесс pwd открывает каталог, стоящий на два уровня выше текущего, и ищет дескриптор с номером 14, которому соответствует имя usr. Теперь путевое имя имеет вид usr/joe. Аналогичный шаг — это поиск в родительском каталоге дескриптора с номером 12. Какое имя ему соответствует? Номер индексного дескриптора родительского каталога — 12, т.е. тот же самый, что и у текущего каталога. В файловой системе таким свойством обладает только один каталог — корневой. Поэтому процесс pwd добавляет косую черту в начало путевого имени, и имя готово: /usr/joe. В этой схеме не учтены два момента. Файловые системы могут быть смонтированы поверх других файловых систем или с помошью сетевой файловой системы. Поэтому на каждом шаге процесс pwd должен проверять устройство, с помощью которого смонтирован текущий каталог. Если вам интересно, просмотрите страницу stat(l) руководства или документацию на свою систему UNIX. Прочтите также последние абзацы параграфа 18.04, где приводятся дополнительные сведения о ссылках на каталоги. - JP 212 Часть третья. Работа с файловой систено*.
14.05 Рис. 14.2. Поиск имени текущего каталога 14.05 Быстрая смена каталога: переменная cdpath : Некоторые пользователи для перехода в часто используемые каталоги создают с помощью ■'■■■' псевдонимов (10.02) собственные команды. Другие для хранения путевых имен каталогов, которые они не хотят набирать помногу раз, устанавливают переменные shell (6.os>. Однако оба метода.требуют от вас запоминания сокращенных имен каталогов, а также определения t . v.-- в-файле .eshrc vim .profile новых пользовательских команд или переменных (это делается каждый раз, когда данные имена нужно изменить). Но существует и более простой способ: использование переменной cdpath в интерпретаторе С shell и переменной CDPATH в • интерпретаторах ksh, bash и некоторых версиях sh. Далее при разговоре о любом типе .интерпретатора shell будет использоваться имя cdpath. '■■■:■.■ Когда вы набираетекоманду cd foo, интерпретатор shell сначала пытается перейти в каталог с указанным именем. Если это ему не удается, a foo является относительным путевым именем, shell пытается выполнить ту же команду из каждого каталога, перечисленного в переменной cdpath. (Если вы работаете с интерпретатором ksh или sh, ознакомьтесь с примечанием в конце этого параграфа.) Предположим, вашим начальным каталогом является /home/lisa, а текущим — какой-нибудь другой каталог. Пусть переменная cdpath содержит имена каталогов /home/lisa, /home/lisa/'projects и /books/troff. Если команда cd foo в вашем текущем каталоге не выполняется, то shell попытается выполнить команды cd home/lisa/foo, cd home/lisa/projects/foo и cd /books/troff/fpo (именно в указанном порядке). После того как будет обнаружен каталог foo, будет выведено путевое имя: % cd foo /home/lisa/foo % Щыяещение по файловой системе 213
Некоторые версии интерпретатора Bourne shell не отображают имени каталога. Однако^ интерпретаторы всех типов выводят сообщение об ошибке, если не могут найти каталог foo. '> Итак, поместим в переменную cdpath список родительских каталогов, содержащих те каталоги '' в которые мы собираемся переходить. Не включайте в него конечные каталоги, а только' родительские (i.2i). Это делается в файле .cshrc или .profile. Например, строка для каталога lisa в файле .cshrc может выглядеть так: 14.11 set cdpath=(~ ~/projects/book/troff) Пользователи Bourne shell должны поместить в свой файл .profile следующие строки: CDPATH=:$НОМЕ:$HOME/projects:/books/troff export CDPATH (Если ваша система не определяет переменную $НОМЕ, попробуйте заменить ее переменной $LOGDIR.) Примечание: Обратите внимание, что имя переменной CDPATH из предыдущего примера начинается с двоеточия (:), которое, как и в случае переменной PATH, является пустой записью (Ш), соответствующей текущему каталогу. Как sh, так и ksh, которые я испытывал, требуют такой формы записи. Без нее ни один из названных интерпретаторов shell не переходит в текущий каталог! (Но интерпретатор bash, как оказалось, работает подобно csh.) Если в переменной CDPATH нет пустой записи, то для перехода в подкаталог текущего каталога нужно воспользоваться командой cd . /подкаталог. - JP 14.06 Команды pushd и popd Как часто вам приходится переходить в другой каталог, просматривать там какой-нибудь файл и возвращаться обратно? По-видимому, такая потребность возникает достаточно часто. Для упрощения подобных переходов в интерпретаторах csh и bash применяются команды pushd и popd. (Если вы работаете с ksh, обратитесь к книге издательства O'Reilly & Associates Learning Korn Shell, где описаны функции, делающие то же самое.) Эти команды реализуют стек каталогов. Классическим аналогом стека является стопка тарелок на подставке в школьной столовой. Тарелка, положенная (pushed) в стопку последней, будет взята {popped) из нее первой. То же происходит и в отношении каталогов: при каждом выполнении команды pushd интерпретатор shell добавляет текущий каталог в стек и переходит в новый каталог. При выполнении команды popd из стека убирается верхний каталог и осуществляется переход в нижележащий. Вы можете изучить команду pushd так же, как это сделал я: путем наблюдений. Предположим^, работая над этой книгой, я нахожусь в каталоге ~/power. При необходимости ненадолго перейти в каталог Mail, с тем чтобы просмотреть старую переписку, я должен воспользоваться следующей командой:* los% pushd -/Mail ...текущим каталогом становится ~/Mail -/Mail -/power Команда pushd выводит весь стек, предоставляя информацию о моем текущем местоположении и сообщая, куда я могу перейти. Просмотрев старую почту, я могу возвратиться: 1оз% popd ...текущий каталогом становится ~/power -/power * Если вы установили переменную cdpath (Ы.М), то в команде pushd можно использовать сокращенные имена- Часть третья. Работа с файловой сиетй!* 214
14.06 Я опять нахожусь в начальном каталоге. Каталога Mail в стеке больше нет. А как быть, если нужно постоянно переходить из одного каталога в другой? Команда pushd без аргументов обеспечивает переход между двумя верхними каталогами в стеке. Например: losi pwd ...текущим каталогом является ~/power /home/los/mikel/power los% pushd -/Mail . . .текущим каталогом становится ~/Ма.И -/Mail -/power los% pushd ...текущим каталогом становится -/power -/power -/Mail los% pushd ...текущим каталогом становится -/MaiJ -/Mail -/power И Т.Д. Вы можете сделать свой стек каталогов достаточно длинным. В таком случае будут использоваться две специальные команды. Команда popd +л удаляет из стека запись номер л. Отсчет записей производится от вершины, начиная с нуля, т.е. текущий каталог имеет номер 0. Поэтому команды popd +0 и popd эквивалентны. Если л больше нуля, то текущий каталог не меняется, так как не меняется вершина стека. Команда pushd +л прокручивает стек таким образом, что каталог номер л становится верхним текущим каталогом. Обратите внимание: происходит именно "вращение" — перемещается весь стек. Команды с опцией +п мне показались не очень важными, но знать о них нужно. Команда dirs выводит стек каталогов и помогает определить имя текущего каталога. Некоторые пользователи любят помещать команду dirs в приглашение (7.п), но меня лично неоправданно длинные приглашения раздражают. Одним из недостатков команд pushd и popd является возможность строить гигантские стеки, заполненные бесполезными каталогами. Я не думаю, что это может каким-то образом навредить вам, но запутать может. Единственный способ очистки стека — повторное выполнение команды popd (исключение составляет интерпретатор tcsh, в котором стек очищает команда dirs -с). Чаще всего каталоги, которые вам нужны, находятся вблизи вершины стека. По-настоящему удобного способа спасти их не существует. Я имею в виду, что при наличии, скажем, 7 каталогов в стеке можно выполнить команды % pushd +5 ; popd ; popd и таким образом избавиться от двух нижних элементов. Команда pushd перемещает два нижних элемента стека, содержащего 7 каталогов, на его вершину. Несколько неудобно. [Для очистки всего стека подходит команда С shell repeat (9.25). Если стек содержит, предположим, 7 каталогов, введите команду % repeat 6 popd Таким образом можно очень легко очистить стек, если он стал слишком громоздким. — JF\ 8 tcsh имеется переменная (6.os) savedirs. Если эту переменную установить, то стек каталогов будет сохраняться в файле ~/.cshdirs при выходе из системы и восстанавливаться при входе в систему. Примечание: Интерпретатор Кот shell имеет несколько аналогичных, однако не настолько развитых средств. В нем сохраняется имя предыдущего рабочего каталога, а для перехода в него выполняется специальная команда cd -. - ML Щюмещеню по файловой системе 215
14.07 14.07 Удобные псевдонимы для команды pushd Команда pushd (им) облегчает перемещение по файловой системе. Но если такие команды, как, например, pushd +4, приходится набирать часто, то это порядком надоедает. Впервые я нашел приведенные ниже псевдонимы (Го.о2) в файле конфигурации, который принадлежал Дэниелу Гилли (Daniel Gilly). Этот прием показался мне настолько удобным, что я решил позаимствовать его для этой книги. Версии определений псевдонимов для С shell приведены слева, а для bash — справа. alias pd pushd alias pd=pushd alias pd2 'pushd +2' alias .pd2='pushd +2' alias pd3 'pushd +3' a'lias pd3='pushd +3' alias pd<3 'pushd +4' alias pd<3='pushd + 4' Теперь, чтобы перейти в четвертый каталог стека, нужно набрать команду pd4. - JP 14.08 Быстрая смена каталога с помощью пользовательских команд Если вы часто работаете в определенных каталогах, то стоит подумать о создании команд, позволяющих быстро переходить в каждый такой каталог. Например, чтобы перейти в каталог /books/troff/pwrtools, я ввожу команду pwr, которая является псевдонимом следующего вида: alias pwr cd /books/troff/pwtools Если ваш интерпретатор shell не поддерживает псевдонимы, можете использовать функции shell (Ю.09). А вот сценарии (i.os) в этом случае работать не будут, поскольку они выполняются в порожденном интерпретаторе shell (зям). Имена псевдонимов необходимо выбирать таким образом, чтобы они не конфликтовали с существующими командами. Как это делается, рассказано в параграфе 44.21. Если у вас есть много псевдонимов для команды смены каталога, можете поместить их в отдельный файл, называющийся, скажем, .cd_aliases. Затем в файл .cshrc нужно добавить следующие строки: source 44.23 alias setcds source ~/.cd aliases - 14.11 , . seLcds Это обеспечит чтение определений ваших команд каждым экземпляром интерпретатора shell. Если вы измените содержимое файла .cd_aliases, то заставить интерпретатор прочесть новый файл можно лишь путем выполнения команды setcds из любого каталога. И наконец, если вы работаете с каталогами, используемыми группой других пользователей, можете создать общий файл псевдонимов команд, который будет читаться из каждого файла .cshrc при входе в систему. Для этого нужно только ввести в них упоминавшиеся выше строки инициализации. - JP 14.09 Смена каталога по его инициалам Представляем вам функцию с именем с, предназначенную в первую очередь для тех, кто постоянно перемещается по файловой системе. (Впервые я увидел ее определение в заметке Марка Брумлика (Marc Brumlik), помешенной в Usenet несколько лет назад. С тех пор мы оба внесли в нее ряд изменений.) Эта функция — просто находка для интерпретаторов shell, не поддерживающих дополнение, имен файлов (9.osi Она делает примерно то же, но быстрее, поскольку с именами каталогов сравниваются только "инициалы" и вам не нужно нажимать клавишу [TAB] или [ESC] после каждой части путевого имени. Вместо этого вводятся инициалы (одна или несколько букв) каждого имени каталога. Начинать нужно с корневого каталога. Между частями, имени ставится точка. 216 Часть третья. Работа с файловой системой
14.09 Приведем три примера. В первом из них предпринимается попытка перейти в отсутствующий подкаталог. Во втором примере находится совпадение заданных инициалов с именем каталога /usr/include/hsfs и выполняется переход в этот каталог: ? с q. : ^ с: по match for /q*/. $ с u.i.h. /usr/include/hsfs/. $ В следующем примере при попытке перехода в каталог /usr/include/pascal инициалы сначала являются неуникальными. Функция показывает мне все случаи совпадения, и в следующий раз я добавляю еще одну букву (а), чтобы сделать инициалы уникальными. $ с u.i.p, с: too many matches for u.i.p. /usr/include/pascal/. /usr/include/pixrect/. /usr/include/protocols/. $ с u.i.pa. /usr/include/pascal/. $ Версия функции с для интерпретатора Bourne shell достаточно проста. Она приведена ниже. Определение псевдонима в интерпретаторе С shell требует использования некоторых ухищрений. На прилагаемом компакт-диске имеется две версии этой функции: одна для sh iirit случая, когда пользовательская команда для смены каталога уже есть, а вторая — когда csHjnit таковой нет. (Оператор i[(4i.03), используемый в команде с, не работал бы в пользовательской команде cd. Хотя руководство по С shell признает, что указанный оператор не будет работать, я бы назвал это еще одной ошибкой С shell (47.02)) с() { dir="$l" # Удаление точек. Окружение каждой буквы символами "/" и "х" % Добавление завершающих символов "/." для обеспечения % совпадения только с именем каталога: dirpat="~echo $dir | sed 's/\ ([А.]*\)\./\/\l*/g'~/." # Если переменная $dirpat пуста, создать временную переменную х, t а затем выполнить сдвиг номеров параметров: ' set 44.19 set x $dirpat; shift % При совпадении перейти в каталог, # в противном случае вывести сообщение об ошибке: if [ "$1' = "$dirpat" ]; then # не найдено соответствие шаблону (shell не сделал подстановки) echo "с: no match for $dirpat" 1>&2 $1*44.15 elif [ $* = 1 ]; then echo n$l" cd "$1" else echo "c: too many matches for $dir:" 1>&2 Is -d "$@" fi unset dir dirpat Работа функции начинается с построения шаблона с метасимволами, соответствующими инициалам имен каталогов. Например, если вы вводите с u.i.h., то программа sed создает шаблон /u*/i*/h*/ и сохраняет его в переменной $dirpat. Далее интерпретатор shell Перемещение по файловой системе 217
14.10 заменяет метасимволы аргументами командной строки. Завершающая точка обеспечивает совпадение только с именами каталогов (как в параграфе 21.12). Если Bourne shell не может найти совпадений с шаблоном, то он оставляет этот шаблон неизменным. Данный факт проверяется оператором // Если обнаружено только одно совпадение, то остается только один параметр командной строки и выполняется переход в этот каталог. Иначе совпадений оказывается много, функция их выводит, и вы можете сделать свой шаблон длиннее и более специфичным. - JP 14.10 Поиск каталогов и файлов с помощью переменных UNIX-системы могут содержать тысячи каталогов и еще больше файлов. Даже если вы помните все путевые имена, набирать их повторно будет непросто. В вашей учетной записи, возможно, уже установлены некоторые полезные переменные среды и интерпретатора shell (6.os,t.t)i). Можно добавить дополнительные переменные — из командной строки или из файлов конфигурации (2.02), например .cshrc или .profile. Чтобы увидеть, какие переменные среды установлены, воспользуйтесь командой env (System V) или printenv (Berkeley). Команда set должна выводить все переменные интерпретатора shell (некоторые из них могут повторяться как системные переменные). Вот фрагмент того, что выводится в моей учетной записи: % env HOME=/home/jpeek MH=/work/jpeek_mail/.mh_profile PATH=/home/jpeek/.bin:/home/jpeek/.bin/show:/work/bin:... RNINIT=/home/jpeek/.rnswitches PAGER=/usr/local/bin/less % set active /usr/lib/news/active cwd /home/jpeek/pwtools mail (60 /usr/mail/jpeek) maillog /usr/spool/smail/log/logfile UNIX-программы используют многие из этих переменных. Например, моя почтовая система находит свой файл конфигурации по переменной МИ. Но я могу применять переменные среды и для других целей. Когда, скажем, нужно отредактировать файл конфигурации электронной почты, я могу из любого каталога ввести vi $MH. Shell подставит в качестве значения переменной строку /work/jpeek_mail/.mh_profile и запустит редактор. Просмотрите список своих переменных, чтобы узнать, чем вы можете воспользоваться. Имена переменных обычно достаточно точно соответствуют их назначению. Интерпретатор shell среди прочих использует и переменную $mail. Я могу просматривать свою почту с помощью команды tail $mai 1 [2] (25.14,47.05) (параметр [2] сообщает С shell, что нужно взять второе слово из списка, содержащегося в переменной $mail). Я установил несколько дополнительных переменных для своих нужд. Отправив почтовые сообщения, я хотел бы иметь возможность следить за системным файлом регистрации почты, с тем чтобы узнать, доставлены ли они по назначению. Для этого достаточно ввести следующую команду: -f 25.16 % tail -f $maillog 09/08/96 17:13:27: [m0kJN4x-O00OAKC] new msg: from jpeel@jpeek.com 09/08/96 17:13:28: [m0kJN4x-O000AKC] <jim> ... delivered 09/08/96 17:13:42: [m0kJN4x-O00QAKC] <allan@comex.com> ... delivered Возможно, у вас есть каталоги, которые посещаются настолько часто, что их не стоит определять с помощью переменной cdpath (U.os) или псевдонимов. Создайте переменную с подходящим именем и добавьте ее в свой файл .cshrc или .profile. В одной переменной можно хранить несколько путевых имен — либо разделив их пробелами, либо используя метасимволы: 218 Часть третья. Работа с файловой системой
i4.ii # Переменные С shell: set worklog=~/todays_worlclog Одиночный файл, имя которого определяется при инициализации set projfiles=(/work/beta/data_3.9*) Несколько файлов, имена которых определяются при инициализации set currdata='/work/beca/data_5»' Насколько файлов, имена которых определяются при использовании # Переменные Bourne shell: worklog=$HOME/todays_worklog Одиночный файл, имя которого определяется при инициализации echo S.06 projf iles=" "echo /work/beta/data_3 . 9_* ' " Несколько файлов, имена которых ,. определяются при инициализации currdata='/work/beta/data_5*' Несколько файлов, имена которых определяются при использовании После этого у вас появится возможность выполнить одно из следующих действий. • В любой момент времени вы сможете ввести команду vi + $worklog, чтобы добавить текст в конец файла todays_worklog в своем начальном каталоге. (Знак + сообщает редактору vi, что текст будет добавлен в конец файла.) • При установке переменной projfiles интерпретатор shell заменяет символ * списком файлов, существовавших на момент установки этой переменной. (Если список файлов изменится, то переменная будет переустановлена при запуске следующего экземпляра shell. Набрав команду lpr $projfiles, вы в любой момент времени сможете все эти файлы распечатать. Интерпретатор С shell позволяет также выбирать отдельные файлы из списка (47.05). Скажем, команда lpr $projfiles [9] распечатает девятый файл из списка. • При установке переменной currdata одинарные кавычки вокруг нее предотвращают интерпретацию (8.N) метасимвола *. Путевое имя /work/beta/da ta_5* раскрывается только при использовании данной переменной. Например, с помощью команды pg $curdata вы можете вывести список файлов, существующих на момент использования переменной. Вы можете также использовать перечисленные переменные для хранения путей к каталогам. С такими переменными применяются команды cd, Is или любые другие. - JP 14.11 Быстрый поиск начального каталога любого владельца Интерпретаторы С shell, ksh и bash применяют тильду (~) как сокращенное путевое имя начального каталога. Тильду можно использовать в путевом имени для ссылки на начальный каталог из текущего каталога. Можно, например, из любого каталога вывести список своего начального каталога или отредактировать файл .cshrc. Для этого достаточно ввести такие команды: % is - % vi -/.eehre Пользователи интерпретатора Bourne shell вместо тильды могут использовать переменные НОМЕ и LOGDIR Перейти из текущего каталога в свой начальный каталог можно, набрав cd ~ или cd $HOME, хотя в большинстве интерпретаторов shell можно просто ввести команду cd (без каких-либо аргументов) и перейти в начальный каталог. Если ваш интерпретатор понимает символ ~, он должен также понимать сокращенные имена начальных каталогов других пользователей, которые даются в виде тильды с последующим именем пользователя. Например, /usr3/usen/mfg/mandi — полное путевое имя начального каталога пользователя Мэнди (mandi) — может быть сокращено до ~mandi. Если Мэнди попросит, чтобы вы скопировали файл menu.c из ее каталога src, то вы должны будете набрать команду % ср -mandi/src/menu/c . Перемещение по файловой системе 219
14.12 Не смущайтесь при встрече с такими именами файлов, как report . Некоторые программы, например редактор GNU Emacs (З2.м), создают временные файлы, заканчивающиеся тильдой (*■). В интерпретаторе Bourne shell имена типа ~mandi не применяются. Приведённый ниже прием выглядит слишком громоздким для постоянного ввода с клавиатуры, но очень удобен в сценариях для Bourne shell, в которых вы не хотите жестко задавать путевые имена пользовательских начальных каталогов. Следующая команда вызывает интерпретатор С shell, чтобы поместить путевое имя начального каталога mahdi в переменную dir: username=mandi dir=4csh -fc "echo ~$username"~ Тильду также очень удобно использовать в файлах конфигурации интерпретатора shell ащ. - JP 14.12 Запоминание текущего каталога в переменной shell Имя текущего каталога записывается в переменную с помощью команды alias mark f set- \ ! : l=-$cwdl и теперь в С shell можно выполнить следующие команды- % mark каталог % cd каталог Набирать $ каталог не нужно. Если каталог отсутствует, то интерпретатор csh пытается найти его имя в переменной cdpath (MM) или, если не находит, интерпретирует метку как имя переменной (s.os, бщ и определяет ее значение. (Для сохранения имен каталогов я, в основном, использую команды, pushd и popd ош). Пользовательская команда mark более удобна для работы с командами, которые должны работать с двумя различными каталогами, и тогда значение $ каталог все равно необходимо. Вот так.) [В интерпретаторе bash то же самое можно сделать, путем установки- переменной cdablejvars. Для этого Достаточно ввести строку cdable_vars=l в свой файл конфигурации shell (uny. — JP\ — СТ, из телеконференции comp.unix.wizards в Usenet, 14 февраля 1987 г. 14.13 Каков же мой текущий каталог? С shell, равно как и некоторые другие интерпретаторы, имеет свое, особое представление о том, в каком каталоге вы находитесь. В интерпретаторе csh полное, путевое имя текущего каталога соответствует значению $cwd, а в bash — значению $PWD^ Но'иногда эти переменные, содержат неправильные путевые имена. Почему это возможно? Потому что переменная cwd была введена задолго до того, как-в UNIX появились символические ссылки qs.M). Как объясняется в параграфе 18.07, символические ссылки могут указывать на каталог, который находится в любом месте файловой системы и даже (в некоторых версиях UNIX) на другом компьютере. "Бедная" переменная cwd не может разобраться: она считает, что текущий каталог имеет имя самой символической ссылки (а не каталога, на который эта ссылка указывает). Это приводит к появлению проблем, подобных описанной ниже: переход в "каталог" с именем wpa, которое на самом деле является символической ссылкой на каталог /work/pwtools/articles. Значение $cwd, выводимое в строке приглашения, является неправильным. А команда /Ь'щ/pwd выводит настоящее имя текущего каталога (ым) (лучше набирать полностью /bin/pwd, поскольку некоторые, версии интерпретатора shell и пользователи используют псевдоним с именем pwd для выполнения команды echo $cwd): /home/jerry% pwd /home/jerry% Is -1 wpa lrwxrwxrwx 1 jerry 23 Sep 8 13:55 wpa -> /work/pwtools/articles /home/jerry% cd wpa . , i 220 Часть третья. Работа с файловой систем^
14.14 /home/jerry/wpa% /bin/pwd /work/pwtools/articles /home/jerry/wpa% \ L В настоящее время многие версии С shell поддерживают переменную hardpaths, которой в интерпретаторе bash соответствует переменная nolinks. Если установить такую переменную (обычно в файле конфигурации (2.в2>), то интерпретатор shell не будет введен в заблуждение символическими ссылками. Смотрите: /home/jerry/wpa% cd /home/jerry% eet hardpaths (в bash, nolinks=l) /home/jerry% cd wpa /work/pwtools/articles% Установка переменной hardpaths или nolinks заставляет shell выполнять дополнительную работу, поэтому не связывайтесь с ними, если вы не используете значение $cwd. С командой dirs (Ы.об) связаны сходные проблемы. Здесь также помогает установка переменной hardpaths или nolinks. Если в системе имеются символические ссылки, но ваш интерпретатор shell не распознает такой переменной, как hardpaths, можно воспользоваться приведенными ниже определениями для файла .cshrc: alias setprompt 'set prompt="${cwd)% "' alias cd 'chdir \\* SS set cwd='/bin/pwd4 SS setprompt' alias pushd 'pushd \\* S& cd .' alias popd 'popd \\* S& cd .' Когда вы вводите команду cd, то соответствующая пользовательская команда переустанавливает переменную cwd в соответствии со значением, возвращаемым командой /bin/pwd, а затем переустанавливает строку приглашения с учетом нового значения переменной cwd. При выполнении пользовательских команд pushd и popd (ым) для перехода в текущий каталог (.) также вызывается псевдоним cd, что приводит к обновлению переменной cwd (то же делает и команда dirs) и переустановке строки приглашения. Ну что? Заслуживают символические ссылки такого внимания? Я думаю, да. - JP 14.14 Автоматическая инициализация при входе и выходе из каталога Если вы работаете во многих каталогах, познакомьтесь со способами организации автоматической инициализации при входе в каталог и возврата в исходное положение при выходе из него. В интерпретаторе bash для этой цели создается функция, версий которой для интерпретатора Кот shell имеется на компакт-диске. alias cd 'if (-о .exit.csh) source .exit.csh; chdir \!*; if (-o .enter.csh) source .enter.csh' cd<> { est hit, test -r .exit.sh ss . .exit.sh Jtfljwf builtin cd "$1" # builtin - эстроенная команда bash test -r .enter.sh SS . . enter.sh } Затем создайте файлы .enter.csh и (или) .exit.csh в тех каталогах, при входе и выходе из которых вы хотите выполнять особую инициализацию. Пользователи Bourne shell должны создавать файлы .enter.sh и (или) .exit.sh. При вводе команды cd с целью перехода V «овый каталог файл .exit выполняется с помощью команды source \44.23) в текущем интерпретаторе shell, перед уходом из прежнего каталога. При входе в новый каталог будет читаться файл .enter, если он существует. Если вы используете команды pushd и popd (М.щ, то, возможно, захотите создать для них псевдонимы или функции, подобные приведенным в примере. Перемещение по файловой системе 221
14.14 u mask 22.04 m .enter.csh, .enter.sh Пользовательская команда cd проверяет, принадлежат ли вам файлы, что помогает избежать сюрпризов со стороны других пользователей. Если в одном каталоге работает много пользователей, то не исключено, что им понадобятся одни и те же файлы. В таком случае замените оператор проверки прав доступа -о оператором -г (выражение истинно, если файлы допускают чтение). Операторы проверки прав доступа, в частности оператор -о, описаны в параграфе 47.04. В интерпретаторах типа sh используется команда test (Шо>, а в интерпретаторе bash — оператор -О. Приведем пример файла .enter.csh. # Сохранить предыдущее значение umask; # переустановить в файле .exit.csh: set prevumask-"umask4 # Разрешить всем членам группы редактировать мои файлы: umask 002 echo ".enter.csh: setting umask to 002" # Установка приглашения (с пустой строкой в начале) # для привлечения внимания set prompt="\ $cwd - PROJECT DEVELOPMENT DIRECTORY. EDIT CAREFULLY...\ if 47.03 %•>. 47.04 Ниже приведен соответствующий ему файл .exit.csh. # Файл .enter.csh мог записать старое значение umask # в переменную shell: if ($?prevumask) then umask $prevumask echo ".exit.csh: setting umask to $prevumask" unset prevumask endif # Напоминание о том, что, возможно, понадобится вернуться назад: echo "If you didn't check in the RCS files, type 'cd $cwd'." # Восстановление исходного приглашения # (псевдоним setprompt определяется в файле .cshrc): setprompt 7.OS setprompt .exit.csh, .exit.sh Примечание: Установка значения umask в файле .enter для некоторого каталога будет действительной, даже если файл в данном каталоге создается из другого каталога с помощью такой команды, как, например, vi /некоторый_каталог/файл. Можно ли один и тот же файл, например .enter.csh или .exit.csh, использовать в нескольких каталогах? Если да, то можно ли сэкономить дисковое пространство и время на дополнительное редактирование путем создания жестких ссылок (is.04> между файлами? Если каталоги находятся в разных файловых системах, то нужно использовать символические ссылки (is.M), хотя это, скорее всего, не позволит сэкономить дисковое пространство. При организации ссылок между файлами не помешает добавить комментарий, который напоминал бы о наличии ссылок при последующем редактировании. Когда ваши файлы .enter станут достаточно длинными, в них можно будет поместить вот такую команду: source 44.23 source ~/.global_enter Файл .globa!_enter из вашего начального каталога содержит процедуру, которая должна выполняться из нескольких файлов .enter. (Некоторые процедуры выполняются из файла .exit)' Еще одна идея: если в одном каталоге работает много пользователей, целесообразно создавать такие файлы, как .enter.joanne, .exlt.allan и т.д. Ваши команды могут проверять существование файла .enter.$user (t.oy (если длина имени файла в вашей системе UNIX ограничена 14-ю символами, то нужно использовать более короткие имена). - JP 222 Часть третья. Работа с файловой системой
15 Метасимволы 15.01 Метасимволы в именах файлов Метасимволы (U6) используются в интерпретаторе shell для сокращения имен файлов. Как и в покере, где джокер (wildcard) является специальной картой, которая может заменить любую карту в колоде, метасимволы (wildcards) в именах файлов могут заменить любой символ или группу символов. Избавляя пользователя от необходимости набирать длинное имя файла или длинную цепочку команд, метасимволы позволяют ему вводить только части имен, а остальное заменять метасимволами. Если, например, вы хотите удалить все файлы с именами, заканчивающимися на .о, то можете ввести следующую команду: % rm *.o Перечислять весь список имен файлов не нужно. Я уверен, что большинство из вас уже знает, насколько удобно пользоваться метасимволами в различных ситуациях. (Их полный обзор приведен в параграфе 15.2.) Назову несколько своих "любимых" случаев применения метасимволов. • Если известна только часть имени файла, то при помощи метасимволов можно найти полное имя. Пусть, например, мне необходимо получить файл по генетике, находящийся в каталоге среди сотен других файлов. Команда % le *gene* как правило, может найти то, что мне нужно. Причем сделает она это быстрее и более простым способом, чем команда find (пм). • Метасимволы оказываются очень кстати при работе с группами файлов. Если каталог общего назначения наполнен файлами с именами, заканчивающимися на .с и .h, я могу создать новые подкаталоги и использовать метасимволы, чтобы с их помощью переместить туда все соответствующие файлы: % mkdir с h % mv *.с с % mv *.h h • Метасимволы помогают при работе с файлами, имена которых содержат символы, отличные от алфавитно-цифровых. Предположим, у нас есть файл abcxe, где х — какой-то неизвестный управляющий символ. Этот файл можно удалить или переименовать с помощью имени abc?e, содержащего метасимвол. (Когда вы пользуетесь этим приемом, будьте внимательны, так как метасимвол может соответствовать большему количеству файлов, чем вы предполагаете.) • Метасимволы могут присутствовать в любой части имени файла. Во многих случаях это зависит от того, как вам удобнее их использовать. Пусть, например, у вас есть каталог /work, разделенный на подкаталоги для десятка различных проектов. Для каждого проекта существует план, содержащийся в файле sckedule.txt. Все планы можно распечатать с помощью команды % lpr /work/VectMdule.txt BSD UNIX % lp /worJc/*/echedule.txt System V UNIX (Однако иногда в подобных случаях возникают проблемы (is.06).) Нпасимвопы 223
15.02 Очень часто пользователи, особенно начинающие, ошибочно предполагают, что приложения и утилиты что-то делают с метасимволами. Они считают, что при вводе grep ident *.c команда grep заменяет символ * и ищет файлы с именами, закачивающимися на . с. Если вы знакомы с принципами работы системы UNIX, то понимаете, что это ошибочное представление. Метасимволы обрабатывает интерпретатор shell. Это значит, что интерпретатор определяет, какие файлы имеют имена, заканчивающиеся на . с, помещает их в список, подставляет этот список в командную строку, а затем передает ее команде grep. При обработке командной строки интерпретатор преобразует grep ident *.c в grep ident filel.c file2.c .... Поскольку существуют различные типы интерпретаторов, то кое-кто может предположить, что имеется много различных наборов метасимволов. К счастью, это не так. В интерпретаторе С shell сделано одно существенное улучшение — добавлен оператор фигурные скобки (()) (9.0S), а в интерпретаторе Кот shell есть ряд других улучшений, но основные метасимволы работают одинаково во всех интерпретаторах. - ML 15.02 Основные метасимволы Ниже дан обзор метасимволов, предназначенных для замены имен файлов. Все интерпретаторы shell используют одинаковые, метасимволы, хотя csh, tcsh, ksh и bash имеют ряд расширений. Если не указано другое, значит, метасимвол действителен для всех интерпретаторов shell. * Соответствует нескольким или ни одному символу. Например, а* соответствует файлам с, ab, abc, abc.d и т.п. ? Соответствует одному символу. Например, а? соответствует файлам aa, ab, ас и т.п. . z] Соответствует любому из символов, перечисленных в скобках. Например, a [ab] соответствует файлу aa или ab. Соответствует всем символам из диапазона от а до z. Например, а [0-9] соответствует файлам a0, al и т.д. Соответствует всем символам, кроме указанных в скобках. Например, а [! 0-9] не соответствует файлу аО, но соответствует файлу аа. Используется только в интерпретаторах типа bash, Kom shell и новых версиях Bourne shell. Соответствует всем символам, кроме указанных в скобках. Например, а[А0-9] не соответствует файлу аО, но соответствует файлу аа. Используется только в интерпретаторе tcsh. Соответствует файлам слово!, слово2 и тд. Например, a_{dog,cat,hors) ) соответствует файлам a_dog, a_cat и ajiorse. Используется только в интерпретаторах bash и С shell. На самом деле эти метасимволы p.os) предназначены не только для обозначения имен файлов. Они могут заменять любую строку, в том числе имена еще не существующих файлов, адреса электронной почты и т.д. Соответствует только одному экземпляру строки abc. Например, х? (abc)x соответствует файлу хх или xabcx. Используется только в интерпретаторе Когп shell. Соответствует нескольким или Ни одному экземпляру строки abc. Например, х* (abc) х соответствует файлам хх, xabcx, xabcabcx и т.п. Используется только в интерпретаторе Kom shell. Соответствует одному или нескольким экземплярам строки abc. Например, х+ (abc) х соответствует файлам xabcx, xabcabcx и т.п. Используется только в интерпретаторе Kom shell. Соответствует любой строке, не содержащей abc. Например, х! (abc)x не соответствует файлам xabcx и xabcabx, но соответствует практически любому другому файлу с именем, которое начинается и заканчивается на х. Используется только в интерпретаторе Kom shell. [12. [a-z] [!ab..z] [ЛаЬ..z] {слово1, слово2 . ?(abc) *(abc) + (abc) !(abc) 224 Часть третья. Работа с файловой систем*
16.03 ^шаблон Соответствует любому имени, которое не" соответствует шаблону. Строка- шаблон должна содержать, по крайней мере, один из метасимволов *, ?, [ ]. Чтобы подходили все имена, кроме одного, можно использовать специальный прием, заключающийся в том, что один символ берется в квадратные скобки. Например, вы можете выбрать все имена, кроме abc, с помощью шаблона ЛаЬ[с]. Используется только в интерпретаторе tcsh. (Для других типов интерпретаторов shell см. сценарий пот (ls.oy.) Обратите внимание, что метасимволы никогда не соответствуют именам файлов с точкой (.), например .cshrc* Это предотвращает случайное удаление (или другие повреждения) таких файлов. Чтобы выбрать эти файлы, введите точку явно. Так, . [a-z]* соответствует любому имени, начинающемуся с точки и строчной буквы. Обратите внимание на шаблон . *. Он соответствует именам каталогов . и .. (см. параграф 15.05, в котором предлагаются способы решения данной проблемы). И последнее замечание. Многие операционные системы (в том числе VAX/VMS и DOS) рассматривают имя и расширение имени как два различных элемента; следовательно, вы не можете использовать один метасимвол для обозначения обоих элементов имени. Что я имею в виду? Допустим, у нас есть файл abc.def. В DOS или VMS, чтобы обеспечить соответствие этому имени, нужно использовать выражение вида *. *. Первая звездочка соответствует имени файла (части перед точкой), а вторая — расширению имени (части после точки). Хотя в UNIX также используются расширения имен, они не рассматриваются как отдельные части имени файла, поэтому одна звездочка (*) будет соответствовать целому имени. - ML, JP 15.03 Использование оператора {} в интерпретаторах Когп shell и Bourne shell Оператор {} (9.os) в интерпретаторах bask и С shell удобен для работы со строками. Некоторые версии интерпретатора Когп shell можно сконфигурировать таким образом, что указанный оператор также будет работать." Если в вашей версии Кот shell этого сделать нельзя или если вы применяете интерпретатор Bourne shell, воспользуйтесь функцией (ю.09) с именем qcsh. (Вы можете переписать ее как сценарий (44.02), если ваш интерпретатор не поддерживает функций.) Эта функция записывает набираемую вами командную строку во временный файл, а затем передает этот файл интерпретатору С shell.*** Введите qcsh, пробел и командную строку, которую вы хотите выполнить. Ниже приведены два примера из параграфа 9.05. В первом из них исправляется опечатка в имени файла (fixbold61.c заменяется fixbold6.c): $ qcsh mv fixbold{61,6} . с Во втором примере выполняется редактирование десяти новых, еще не существующих файлов: $ qcsh vi /usr/foo/file{a, Ь, с, d, a, f, g, h, i, j} Вот эта функция: qcsh qcsh () { echo "$@" > /tmp/q$$ csh -f /tmp/q$$ {23.10 rm -f /tmp/q$$ } - JP * Установка в интерпретаторе bash переменной glob dotJilenames обеспечивает возможность заменять подобные имена файлов с помощью метасимволов. " Если в вашей системе есть исходные тексты Кот shell, ваш системный администратор может отредактировать файл OPTIONS, установив переменную BRACEPAТравной 1, а затем выполнить перекомпиляцию. ***В некоторых версиях UNIX передача интерпретатору С shell командной строки csh -fc "$@" не приведет к замене фигурных скобок. Вот почему я использовал временный файл. Метасимволы 225 8 9-171
15.04 15.04 Когда для метасимвола нет совпадений Однажды я попал в странную ситуацию. Компилируя программу, создающую файл дампа (S3.ui), я в какой-то момент решил удалить объектные файлы и файл core. Я ввел команду % гш *.о core Но эта команда не сработала, так как не было обнаружено ни одного существующего объектного файла. (Я не помню, почему так вышло; возможно, я ввел метасимволы ! I (п.щ, зная, что нет ни одного файла с расширением .о.) В этом случае выводится сообщение No match, а файл core не удаляется. Оказывается, что если интерпретатор С shell не находит соответствия ни для одного метасимвола, то возникает ошибка No match (нет совпадения). И не имеет значения, что для других имен файлов совпадение существует. Дело в том, что если интерпретатор csh не может найти соответствия для метасимвола, то он прекращает выполнение команды и выводит сообщение об ошибке. Если вы создадите один файл с расширением .о или уберете из командной строки аргумент .о, то файл дампа будет успешно удален. С другой стороны, когда интерпретатор Bourne shell не может найти соответствия для метасимвола, то он передает его вместе с другими именами *.о core команде (в данном случае rm) и предоставляет ей право самой решить, что с ним делать. Поэтому в среде Bourne shell дальнейшее развитие событий будет зависеть от того, что сделает команда, если ей попадутся символы *.о. Поведение интерпретатора csh можно сделать во многом похожим на поведение интерпретатора sk, если выполнить команду set nonomatch - ML, JP 15.05 Выбор с помощью метасимволов "скрытых" файлов Если вы хотите выбрать в каталоге все файлы с именами, не начинающимися с точки (.), то сделать это просто: достаточно воспользоваться звездочкой (*). А как быть с файлами, имена которых все-таки начинаются с точки? В данном случае произвести выборку сложнее, потому что точка со звездочкой (. *) также соответствует ссылкам на каталоги с именами . и . ., которые есть в каждом каталоге и обычно вас не интересуют. Интерпретатор Кот shell и некоторые версии интерпретатора Bourne shell, так же как и bash, для выбора файлов с точкой позволяют использовать последовательность .[!•]*, где [!. ] означает "все, кроме точки". Интерпретатор tcsk понимает последовательность . [А.]*. Вы спросите, какие еще варианты возможны? Рекомендуем воспользоваться шаблоном . ??*, который соответствует всем именам файлов, начинающимся с точки и содержащим как минимум два других символа, но не соответствует таким именам, как .с — только с одним символом после точки. Можно предложить следующее решение: . [ЛА—0-Л?]* Это выражение соответствует всем именам файлов, второй символ которых присутствует в таблице символов ASCII (Si.oj) и не является точкой (.) или косой чертой (/). Первый диапазон имен файлов начинается комбинацией клавиш [CTRL-a] (ЛА является одним символом [CTRL-a], а не двумя символами — Л и А) и заканчивается символом дефиса. Второй диапазон начинается символом 0 и заканчивается символом [DEL] или [CTRL-?], который можно получить в результате нажатия клавиши [DELETE] или [RUBOUT]. (Возможно, предварительно нужно набрать [CTRL-v] или обратную косую черту.) Да, все это выполнить сложно. Чтобы упростить процедуру, я поместил приведенную выше последовательность в переменную dots в своем файле конфигурации (2.02). Ниже даны три 226 Часть третья. Работа с файловой системой
15.07 версии определения этой переменной. Третья из них предназначена для тех интерпретаторов shell, в которых встроенная команда echo не понимает последовательности \ппп. set dots=".??* .['echo Y—0-Z | tr YZ \\001\\177' ] " csh dots-", ['echo \\\\001 —0-\\\\0177 ' ] *" sft и т.п. dots=".['echo Y—0-Z | tr YZ \\001\\177']*" sh со старой командой echo shlnlt (Команда tr между обратными кавычками (9.щ превращает выражение Y—0-Z в ряд символов, содержащий нужные нам [CTRL-a] и [DEL]. Это предотвращает введение в файл xshrc специальных, непечатаемых символов; см. параграф 45.35.) Таким образом, я могу переместить все файлы из текущего каталога в какой-либо другой каталог путем ввода команды % mv * $dots /каталог - JP 15.06 Метасимволы в путевых именах использовать не рекомендуется Предположим, вы вводите команду, подобную приведенной ниже (необязательно rm — это относится к любой команде UNIX): % пп /каталог/подкаталог/* Пусть под этот шаблон подходит, скажем, 100 файлов. Команда rm получает от интерпретатора shell 100 полных имен: /каталог/подкаталог/файл1, /каталог/подкаталог/файл2 и т.д. Поиск каждого из этих файлов адро системы UNIX должно начинать с корневого каталога, затем искать каталог, подкаталог и только после этого — файл, который нужно удалить. На это может уйти довольно много времени, особенно если диск занят выполнением каких-то других команд. Целесообразнее сменить каталог и уже оттуда запустить команду rm. Это можно сделать в интерпретаторе shell, запускаемом в порожденном процессе (с помощью скобок) (13.07), поэтому вам не придется возвращаться в изначальный каталог: % (cd /каталог/подкаталог; пп *) Воспользовавшись данным приемом, вы получите еще одно преимущество: появление сообщения об ошибке Arguments too long (слишком длинные аргументы) станет менее вероятным. (Другим способом обработки длинных командных строк является применение команды xargs (9.2i).) - JP 15.07 Получение списка подходящих файлов с помощью команды grep -I Обычно, когда команда grep (27.01) применяется по отношению к группе файлов, на выходе мы получаем список имен файлов вместе со строками, содержащими искомый шаблон. Часто бывает, что нужно знать только имена файлов, а совпадающая строка (или строки) нас не интересуют. В таком случае рекомендуется использовать опцию -/, обеспечивающую вывод списка имен файлов, в которых обнаружены совпадения. Например, команда % grep -1 R5 файл1 файл2 ... > r5.filelist ищет файлы, содержащие строки, в которых есть подстрока R5, создает список имен файлов и помещает его в файл rS.jllelist. (Этот список может включать файлы документации для Release 5 ло определенному продукту.) Поскольку к этим файлам теперь можно обратиться по одному списку, то их можно рассматривать как один объект, применяя одновременно разнообразные команды: '...' 9.16 % print 'cat r5.filelist' Распечатка файлов, относящихся к Release 5 % grep UNIX 'cat r5.filelist' Поиск в файлах, относящихся к Release 5 Однако создавать файл со списком необязательно. Вы можете поместить выходные данные команды grep непосредственно в командную строку при помощи механизма подстановки Метасимволы 8* 227
15.08 результатов выполнения команды. Например, чтобы отредактировать только подмножество файлов, содержащих R5, можно ввести команду % vi "grep -l R5 файла' Команда grep -I удобна также для использования в сценариях, которые должны проверять наличие в файлах определенных строк. Традиционный метод выполнения такой задачи состоит в подавлении выходных данных команды grep и проверке ее кода завершения: if grep текст файл >/dev/null then . . . Однако команда grep должна просмотреть текст полностью, даже если он слишком длинный. Добавление опции -/ позволяет сэкономить время, поскольку поиск может быть прекращен после того, как будет найдена первая подходящая строка. - DG, JP 15.08 Получение с помощью команды grep -с списка файлов, не содержащих шаблона Опцией -с команды grep (27.02) можно воспользоваться, когда необходимо выяснить, сколько раз встречается заданный шаблон в определенном файле. С ее помощью можно найти и такие файлы, которые не содержат заданный шаблон (т.е. он встречается в них 0 раз). Предположим, вы вставляете индексы в документ программы troff (43.13) и хотите создать список файлов, не содержащих макросы индексирования. Для этого нужно найти файлы, в которых отсутствуют строки .XX. Команда % grep -с "\.ХХ" chapter* может вывести следующий список: chapterl:10 chapter2:27 chapter3:19 chapter4:0 chapter5:39 Это все хорошо, но представьте себе, что нужно проверить наличие индексов на сотнях страниц списка? В таком случае следует просто отфильтровать выходные данные команды grep, передав их по каналу другой команде grep. Приведенную выше команду можно изменить следующим образом: % grep -с "\.ХХ" chapter* | grep :0 В результате получим такой результат: chapter4:0 Использовав редактор sed (34.24) для отсечения символов :0, можно сохранить выходные данные как список файлов. Вот как выглядит командная строка для создания списка файлов, не содержащих макросов индексирования: % grep -с "\.ХХ" * | sed -n s/:0//p > ../not_indexed.list Команда sed -п выводит только строки, содержащие : 0, одновременно удаляя : 0 из выходных данных, так что файл ../notjndexed.list хранит список файлов, по одному в каждой строке. Путевое имя . . (i.2i) обеспечивает помещение файла notjndexed.list в родительский каталог — это один из простых способов предотвратить поиск командой grep строк в этом файле, но так делать необязательно. [При необходимости отредактировать все файлы, в которые нужно вставить макросы индексирования, можно ввести команду: % vi grep -с "\.ХХ" * | sed -n s/:0//p" 228 Часть третья. Работа с файловой системой
15.09 Эта команда станет вам более понятной, когда вы узнаете, для чего используются обратные кавычки. Данную строку можно поместить в небольшой сценарий с именем vgrep, добавив элементы, обеспечивающие безопасность: #!/bin/sh case $# in Oil) echo "Usage: 'basename $0~ pattern file [files...]" 1>S2 ;; *) pat="$l"; shift grep -c "Spat" "50" I sed -n 's/:0$//p' esac Затем можно набрать, например, vi "vgrep "\.XX" *\ — JP\ - DG 15.09 Сценарий пот: список файлов, не соответствующих шаблону Сценарий пот (от no match — нет соответствия) берет имена файлов (обычно подставленные интерпретатором) из командной строки и выводит все имена файлов текущего каталога, которые не соответствуют шаблону. Как говорилось в параграфе 15.02, интерпретатор ksh поддерживает оператор !, который работает примерно как сценарий пот, tcsh поддерживает оператор ^шаблон, но в интерпретаторах других типов подобных средств нет. Приведем примеры использования сценария пот. • Получение имен всех файлов, которые не заканчиваются на .ms: % nam *.ms • Редактирование всех файлов, имена которых не содержат прописных букв (применяется подстановка результатов выполнения команды (9.16)): % vi 'nam *[a-z]*~ • Копирование всех файлов в каталог Backup (кроме самого каталога Backup): % ср 'пот Backup' Backup Вот текст сценария: # !/bin/sh temp=/tmp/NOM$$ stat=l # Код возврата при ошибке (устанавливается # в 0 в случае успешного завершения) trap 'rm -f $temp; exit $stat' 0 1 2 15 # Должен быть хотя бы один аргумент. # Все файлы должны находиться в текущем каталоге: case "$*" in "") echo Usage: 'basename $0" pattern 1>S2; exit ;; */*) echo ""basename $0' quitting: I can't handle Vs." 1>£2; exit ; ; esac # Получение имен, которые не должны совпадать с шаблоном; # замена пробелов символами новой строки: echo "$*" I tr • ' '\012' | sort > $temp # Сравнение с текущим каталогом (-1 — одно имя на строку); # вывод имен, которые нам нужны: Is -1 I comm -23 - $temp stat=0 Вы можете удалить опцию -1 команды Is, если ваша версия этой команды по умолчанию выводит в строке имя одного файла. Почти все версии команды Is делают так, когда выполняют запись в канал. Обратите внимание, что сценарий пот не учитывает существования файлов, имена которых начинаются с точки (.). При желании вы можете исправить это упущение, добавив опцию -А команды Is (эта опция есть не у всех версий команды). case 44.06 $• 44.15 coram 28.12 - 13.13 Метасимволы 229
15*10 Строка сценария с командой tr (ss.io будет разбивать на составляющие имена файлов, содержащие пробелы. Вы можете заменить эту строку тремя строками, приведенными ниже. Правда, они выполняются не всеми версиями интерпретатора shell и сравнительно медленно, но способны исправить эту нечасто возникающую проблему: for 44.I6 for file do echo "$file" done 145.23 done | sort > $temp - JP 15.10 Шаблоны для поиска каталогов Как известно, интерпретатор shell превращает метасимволы . *, т.е. точку со звездочкой, в имена файлов из текущего каталога, начинающиеся с точки: .login, .profile, .bin (так я назвал свой каталог) и т.д., в том числе . и .. . Многие также знают, что интерпретатор shell превращает символы */ .* в список скрытых файлов, которые находятся в подкаталогах, напримерfoo/.exrc, foo/hidden, bar/лхх, а также foo/, foo/., bar/, и bar/.. (Если это вас удивляет, внимательно посмотрите на строку метасимволов или попробуйте выполнить команду echo: echo */.*.) А что получится, если интерпретировать метасимволы как имена каталогов, а не файлов, которые в них находятся? Самый простой пример — это выражение * /., соответствующее каталогам foo/, bar/, и др. Запись с именем . в каждом каталоге является ссылкой на сам этот каталог (is.02, ы.04), поэтому ее можно использовать для определения имени каталога. Так, чтобы получить список имен подкаталогов, нужно ввести следующее: $ is -d */. bar/. foo/. Опция -d (16.08) сообщает команде Is, что нужно выводить список имен каталогов, а не их содержимое. В некоторых (но не во всех) версиях интерпретатора С shell конечная точка в команде не нужна: % is -d */ bar/. foo/. (Интерпретатор shell передает косую черту (/) команде Is. Поэтому при использовании опции ^F (16.12) команды Is, обеспечивающей присутствие косой черты после имени каталога, в списке будут приведены имена каталогов с двумя косыми чертами в конце каждого из них.) При сравнении имен каталогов, начинающихся с точки, интерпретатор shell выполняет замену метасимволов .*/ или .*/. и передает результаты команде Is, поэтому опция -а (i6.ii) этой команды практически является ненужной. Опция -а полезна только в том случае, если именно с помощью команды Is (а не самим интерпретатором shell) читается каталог и выводится список содержащихся в нем записей. Для этого вовсе не обязательно использовать команду Is. Тот же список проще получить с помощью команды echo (з.ов). Приведем еще один пример — цикл в интерпретаторе Bourne shell, выполняющий команду в каждом подкаталоге вашего начального каталога: for dir in $HOME/*/. do cd $dir ...Некоторые команды... done В этом цикле подкаталоги с именами, начинающимися с точки (например, .bin), не обрабатываются. Как работать с подобными каталогами, рассказывается в параграфе 15.05. В параграфе 21.12 описан прием, посредством которого создаются путевые имена, соответствующие только каталогам. Возможности интерпретатора shell и метасимволы при этом не используются. - JP 230 Часть третья. Работа с файловой системой
16 Где же эти файлы? 16.01 Все, кроме команды find Компьютер похож на дом или офис в том смысле, что если не придерживаться строгого порядка, то на поиск оставленных в ненадлежащем месте вещей приходится тратить много времени. И даже если вы придерживаетесь определенного порядка, на поиск нужных вещей все равно уходит какое-то время — просто вы по-своему представляете, где они могут быть. Но ведь библиотекари не запоминают точное местоположение каждой книги, а просто знают, как можно быстро найти любую книгу, используя доступные средства. Чтобы стать профессиональным пользователем системы, очень важно уметь быстро находить те или иные ее компоненты. Именно этому вопросу и посвящена данная глава. Мы исключим из обсуждения саму утилиту find O7.01), так как она сложна и заслуживает рассмотрения в отдельной главе, и сосредоточимся на описании более простых способов поиска файлов, начиная с различных вариантов использования команды Is. - ML 16.02 Поиск старых и новых файлов Каталог может содержать 50, 100 и более файлов. Какие файлы давно не использовались? Может быть, можно освободить место, удалив их? Вы редактировали файл вчера, но не помните его имя? Описанные в этой главе команды помогут найти и такие файлы. (Если хотите узнать, какие временные характеристики связаны с файлами в UNIX, обратитесь к параграфу 16.05.) Рассмотрим в качестве примера мой каталог .bin (4.02), наполненный сценариями и другими программами, которые я использую нечасто. Описанные ниже способы поиска вы можете применить по отношению к текстовым или другим файлам. Команда Is имеет ряд опций, изменяющих способ упорядочивания файлов в списке. По умолчанию она выводит список файлов в алфавитном порядке. Это вряд ли поможет найти старые файлы, но послужит хорошим началом для объяснения. Для поиска старых файлов предназначена опция -t. Она позволяет сортировать файлы согласно времени последней модификации. Сначала в списке идут самые новые файлы. Вот как это выглядит: jerrySora - 60 % Is -t weather crontab rhyes rhno pickthis cgrep dirtop which tcx -/.bin unshar zloop showpr rfl maillog c-w cw. ex showmult scandrafts tofrom incc drmm reheader zrefile zscan zfolders alifile rn2mh rmmer mhadd fixsubj disprompter xrahprint replf fols incs recomp mhprofile append README rtfm saveart echoerr Всего лишь вчера я создал новый файл weather. Вы видите его в первом столбце на первом месте. На прошлой неделе я вносил изменения в сценарий crontab — он показан следующим. где же эти файлы? 231
16.02 Самой старой программой здесь является представленная в списке последней программа echoerr* Команда Is -t также великолепно подходит для использования в сценариях (2.is, I6.27). [Лично для меня команда Is -t кажется наиболее полезной в том случае, если я забываю, был ли недавно отредактирован тот или иной файл. Если файл был изменен, то он окажется в выведенном списке на первом месте (или на одном из первых). Можно, например, задаться вопросом: "Внес ли я изменения в письмо, которое собираюсь отправить?" Если я этого не сделал, то, скорее всего, соответствующий файл появится где-то посредине списка. — ML] Опция -и задает вывод списка файлов в соответствии со временем последнего доступа, а не последней модификации файла. Эта опция не может использоваться просто с командой Is ~~ ее нужно применять совместно с другой опцией, например -t или -/. Из следующего списка видно, что недавно я работал с файлами rtfn и гттег, но давно не читал файл README. jerry@ora -/.bin 62 % Is -tu rtfm r miner rfl mhprofile showrault tcx tofrom rn2mh weather ex c-w cw ciisprompter recorap crontab rahadd pickthis mcc drmm zscan zrefile xmhprint zloop zfolders which unshar showpr saveart scandrafts rhno rhyes replf reheader incs maillog fols fixsubj echoerr dirtop eg rep append alifile README Некоторые версии UNIX не обновляют время последнего доступа к исполняемым файлам (21.os) при их запуске. Сценарии читаются всегда, поэтому значения их времени доступа всегда обновляются. Опция -с обеспечивает вывод даты последнего изменения индексного дескриптора (1.2г,2\.щ. Это значение показывает, когда файл был создан, когда при помощи команды cfmod изменялись права доступа к нему и т.д. Но эта опция не поможет найти устаревшие файлы. jerry@ora 64 % Is - weather crontab eg rep zloop dirtop pickthis rhno unshar rhyes -/.bin tc maillog tcx zscan zrefile rfl showmult rtfm incc zfolders reheader rn2mh tofrom mhadd drmm alifile showpr scandrafts xrahprint recomp fols rmmer fixsubj mhprofile append saveart disprompter replf incs ex cw c-w echoerr which README Если вы хотите узнать, как давно был изменен файл (или когда был доступ к нему), добавьте опцию -/, обеспечивающую вывод полного списка. Как и прежде, добавление опции -и обеспечивает вывод последнего значения времени доступа, а использование опции -с позволяет получить время последнего изменения индексного дескриптора. Взглянув на значения времени доступа к конкретным файлам, я обнаружил, что не читал файл README с 1989 года: jerry@ora -/.bin 65 % Is -ltu README alifile maillog -rwxr-xr-x 1 jerry ora 59 Feb 2 -rwxrwxr-x 1 jerry ora 213 Nov 29 -rw-r—r— 1 jerry ora 3654 Nov 27 1991 maillog 1989 alifile 1989 README - JP В некоторых системах команда Is -I выводит список файлов в один столбец, с самым новым файлом на первом меси. Хотя это может показаться неудобным, мне такой список нравится, но при условии, что меня интересуют самые последние файлы. Если вывод в один столбец вас не устраивает, воспользуйтесь командой Is -Of. В других системах, если необходимо получить вывод в один столбец, можно применить команду Is -It. где / означает "один столбец • В данной главе мы предполагаем, что вывод производится в несколько столбцов.- 232 Часть третья. Работа с файловой сиспно*
16.03 1603 Перекомпоновка списка, выведенного командой Is В предыдущем параграфе я познакомил вас с несколькими способами сортировки выходных данных команды Is на основе временных характеристик. При этом последовательность их представления оставалась постоянной: списки выводились на экран по колонкам, в направлении сверху вниз и слева направо. Но такой порядок вывода списков не всегда бывает удобным. Скажем, если вы собираетесь просматривать каталог с очень большим количеством файлов, то, наверное, более удобным было бы компоновать список слева направо и сверху вниз. Особенно целесообразен такой подход при использовании программ постраничного вывода (например, pg или тоге (25.оз)). Тогда первые файлы будут расположены в верхней части страницы. Рассмотрим пример: jerrySora -/.bin 59 % Is -x README crontab drmm incs recomp rhyes scandrafts unshar lloop I pg alifile cw echoerr maillog' reheader rmmer showmult weather zrefile append ex fixsubj mhadd replf rn2mh showpr which zscan c-w dirtop fols mhprofile rfl rtfm tcx xmhprint eg rep disprompter incc pickthis rhno saveart to from zfolders Этот список упорядочен по алфавиту, а не по времени модификации файлов. Поэтому на первом месте находится файл README (прописные буквы идут впереди строчных), а за ним — alifile. Опция -х обеспечивает вывод в несколько столбцов. В BSD UNIX нет опции -х, поэтому в этой системе для получения списка, упорядоченного по тому же принципу, вывод команды Is необходимо передать по каналу сценарию cols os.ia). И в'BSD, и в System V есть опция -С, обеспечивающая сортировку столбцов в направлении сверху вниз, а не слева направо. Причем эта опция в BSD используется по умолчанию, если вывод команды Is не перенаправляется. Если команда Is в BSD обнаружит, что данные выводятся не на терминал, то она переключится с вывода в несколько столбцов на вывод в один столбец. В BSD, чтобы обеспечить вывод в несколько столбцов при передаче по каналу вывода команды Is, необходимо использовать опцию -С (либо другие средства, например сценарий cols или команду рг -число (35.17)). Опция -г обеспечивает вывод в обратном порядке. Я обнаружил, что это особенно удобно при просмотре времени модификации файлов. Опция -t обеспечивает вывод первыми имен самых новых файлов, а вот опция -//• задает вывод сначала имен самых старых файлов: jerrySora 61 % Is -< echoerr saveart rtfm README append fols mhprofile recomp incs -/.bin tr replf xmhprint disprompter fixsubj mhadd rmmer rn2mh alifile zfolders zscan zrefile reheader drmm incc tofrom scandrafts showmult cw c-w ex maillog rfl showpr zloop unshar tcx which dirtop cgrep pickthis rhno rhyes crontab weather Добавление опции -и приводит к выводу первыми имен файлов, доступ к которым осуществлялся позже, чем к остальным: , jerry@ora -/.bin 63 % Is -tur README maillog which mhadd tofrom Где же эти фаты? 233
16.04 alifile append cgrep dirtop fixsubj echoerr fols incs replf reheader rhyes rhno showpr scandrafts saveart unshar zfolders zloop xmhprint zscan zrefile drmm incc pickthis crontab recomp disprompter cw ex c-w weather rn2mh tcx showmult mhprofile rmmer rfl rtfm - JP, ML 16.04 Вывод списка содержимого всех подкаталогов Команда Is по умолчанию выводит список содержимого одного каталога. Если в командной строке указать один или несколько каталогов, то будет выведен список содержимого каждого из них. Опция -R обеспечивает рекурсивный вывод списка содержимого всех подкаталогов. При этом представляется все дерево каталогов, начиная с текущего каталога (или каталога, указанного в командной строке). Такой список может быть довольно длинным, и, возможно, имеет смысл направить его по каналу программе постраничного вывода, например more (2S.ai). Команда Is -С также очень хорошо подходит для вывода списка в виде столбцов. (Когда вывод команды Is направляется в канал, то в BSD-версиях UNIX колонки автоматически создаваться не будут.) - JP 16.05 Временные характеристики файлов UNIX В разговоре с опытными пользователями UNIX часто можно услышать небрежно произносимые термины "время изменения" и "время модификации". Для большинства пользователей (а также в соответствии со многими словарями) "изменение" и "модификация" — это одно и то же. А есть ли между ними разница на самом деле? Разница между временем изменения и временем модификации такая же, как разница между изменением этикетки и содержимого коробки. Если задается команда chmod a-w myfik, то происходит изменение; если же задается команда echo foo » myfile, то выполняется модификация. При изменении меняется содержимое индексного дескриптора о.22) файла, а при модификации меняется сам файл. [Время модификации также называется временной меткой. — JP] Раз мы заговорили о времени изменения и времени модификации, нужно упомянуть и о времени доступа. Время доступа указывает, когда файл был прочитан или записан в последний раз. Поэтому при чтении файла изменяется его время доступа, но остается прежним его время изменения (информация о файле не изменилась) и время модификации (не изменилось содержимое файла). Время изменения файла во многих документах, в том числе и в некоторых руководствах по UNIX, ошибочно называют "временем создания". Помните об этом. - СТ 16.06 Сценарии elf и els: "сжатые" списки Большинство новейших UNIX-систем позволяют создавать имена файлов размером в несколько сотен символов. Когда команда Is перечисляет такие имена в столбцах, то ширина экрана не позволяет видеть все столбцы одновременно. Я написал сценарий, который выводит список содержимого каталога в пяти столбцах. Если имя файла не помещается в столбце, то оно усекается и в конце выводится угловая скобка (>)■ Рассмотрим приведенный ниже пример. Он начинается со стандартной команды Is с опцией ^£ 06.12), которая помечает имя каталога конечным символом /, а исполняемые файлы — 234 Часть третья. Работа с файловой системой
16.06 символом *. Далее сценарий elf выводит тот же список в сжатом виде. В следующем списке, выведенном сценарием els, символы /и * отсутствуют. is -f HOMEDIR_backup/ adir/ afile cfile dfile file with a ver jerry MH.tar.Z % elf HOMEDIR_back>/ adir/ afile % els HOMEDIRJbackup adir afile у long name what cfile dfile file_with_a_v>* cfile dfile file_with_a v> more* projects.1995/ projects.1996/ updatedb.client zfile a mess* zoo.tar.Z jerry_MH.tar> projects. more* updatedb. projects.1995/ zfile jerry_MH.tar> projects. more updatedb. projects.1995 zfile ,1996/ .clie> .1996 ,clie> zoo.tar.Z zoo.tar.Z Сценарий имеет четыре имени (ссылки). По команде els выводится отсортированный список в виде колонок. Вывод команды elf похож на вывод команды els, но в отличие от последней в нем помечены каталоги и исполняемые файлы. Команды cls2 и clf2 подобны командам els 'ds ~~" и elf но располагают имена файлов построчно. Так получается быстрее, но затрудняет чтение списка. Сценарий проверяет имя, под которым он вызван, и выполняет команды в условной конструкции case (иол), начинающейся следующим образом: case "$0" in *clf2) $ls -F ${l+"$@"} I sed -e "$sed" I$pr -11; exit;; Значением выражения ${1 + "$@"} является список имен файлов из командной строки, не разбитый на отдельные имена. Это позволяет обойти различия в принципах обработки пустой строки параметров $@ некоторыми старыми версиями интерпретатора Bourne shell (46.07). Изюминкой сценария является двухстрочная команда sed (34.24), приведенная ниже (одиночные кавычки вокруг выражения обеспечивают передачу обеих строк за раз в переменную интерпретатора shell): sed=V[/@* = ]$/s/A\( \) ...*\([/@*=] [/@*=]*\)$/\1>\2/ з/Л\( \) ...*/\1>/* Вывод команды Is направляется по каналу иа стандартный ввод команды sed. • В первой строке сравниваются строки длиной более 14 символов, заканчивающиеся символом =, /, @ или *. Оператор \ (.. Л) (Шо) направляет первые 12 символов в переменную \1, а конечный символ — в переменную \2, после чего значения переменных выводятся с символом > между ними. • Во второй строке сравниваются строки длиной более 14 символов, не заканчивающиеся одним из перечисленных выше символов. (Причем имена файлов, выбранные в первой строке команды, на этот раз не будут соответствовать шаблону, так как они укорочены.) В данной строке первые 13 символов выбираются и выводятся с символом > в конце. Разобравшись во всем этом самостоятельно, можете передать своему менеджеру, что вы, на мой взгляд, заслуживаете повышения. : -). Другой интересной деталью сценария является следующая строка: \.\4S.31 $pr -l'expr \( Vwc -1 < $terap\'/5\) + 1' $terap Она используется, когда вызывается команда elf или els и имена файлов должны выводиться в колонках в направлении не слева направо, а сверху вниз. Строка подобного вида используется и объясняется в параграфе 35.16. Так как на ее выполнение тратится определенное время, команды elf и els работают немного медленнее по сравнению с командами clf2 и cls2. Где же эти файлы? 235
16.07 Вы можете инсталлировать этот сценарий с компакт-диска или из сетевых архивов ря.от). Если вы получите сценарий из архива, используйте программу tar, что позволит вам инсталлировать сценарий els и три другие ссылки на него: % tar xvf архив, tar els elf cls2 elf2 x els, 1282 bytes, 3 tape blocks elf linked to cols cls2 linked to cols clf2 linked to cols - JP 16.07 Сокращения, используемые при вызове команды Is с различными опциями В старой системе 4.1 BSD UNIX, на которой я работал в 80-х годах, были команды, исчезнувшие по крайней мере в BSD, с именами // (для Is -/), If (для Is -F) и lm (для Is -m). [Для тех, кто забыл, напоминаю, что команда Is -m выводила список файлов, разделенный запятыми, а не пробелами. — ML] Когда эти команды в моей системе исчезли, я создал сценарий, выполняющий те же задачи. Если этого сценария в вашей системе нет, инсталлируйте его с компакт-диска. Для всех команд используется один и тот же сценарий: #! /bin/sh case $0 in *lf) exec Is -F "$@";; *lg) exec Is -lg "$@";; *11) exec Is -1 "$&";; *lm) Is -1 "$@" | sed -n -e ,s/$/,/' -e H -e ^g' -e '$s/\n//g< -e '$s/,$//p' t t *lr) exec Is -1R "$@";; *) echo "$0: Help! Shouldn't get here!" 1>&2; exit 1;; esac Команда exec (45.o?) позволяет не запускать новый процесс, что было важно для моей перегруженной системы VAX 11/750, но для более быстрых систем значения не имело. Этот сценарий можно инсталлировать с компакт-диска, а можно просто набрать с клавиатуры. Если для сценария вы создали файл с именем If не забудьте создать четыре ссылки (/лед — с именами lg, //, lm и lr. Сценарий проверяет имя, при помощи которого он был вызван, чтобы решить, с какими опциями вьтолнить команду Is. Этот прием позволяет сэкономить дисковое пространство. В System V все еще имеется опция -т, поэтому раздел сценария *1т) можно заменить обычной командой lm -т.* В некоторых версиях UNIX добавление опции -g никак не сказывается на выводимой информации. Замените соответствующий раздел сценария командой Is -1G или Is -lo. Можно также ввести другие команды, добавив строку в конструкцию case и создав еще одну ссылку. (Дополнительные сведения по программированию в интерпретаторе shell можно получить в параграфе 44.01.) - JP С> 16.08 Команда Is -d Если аргументом команды Is является путевое имя каталога, то она выведет список элементов этого каталога: % Is -1 /home/Joanne total 554 В этом случае можно обойтись псевдонимами (им команды Is. — Примеч. ред. 236 Часть третья. Работа с файловой системой.
16.09 -rw-r—г— 1 Joanne 15329 Oct 5 14:33 catalog -rw 1 Joanne 58381 Oct 10 09:08 mail С опцией -d команда Is выводит только информацию о самом каталоге: % Is -Id /home/joanne drwxr-x—x 7 joanne 4608 Oct 10 10:13 /home/joanne Опция -d особенно удобна для вывода списка имен каталогов, которые заданы шаблоном. Сравните списки, полученные с опцией -d и без нее: % Is -Fd [a-c]* arc/ bm/ Ctrl/ atcat.c cdecl/ atl.c.z cleanscript.с % Is -F [a-c]* atcat.c atl.c.z cleanscript.с arc: BugsEtc.Z arcadd.c arcext.c.Z arcmisc.c.Z bm: Execute.c.Z MakeDesc.c.Z MkDescVec.c.Z Search.c.Z Как вывести только имена каталогов, вы можете узнать из параграфа 15.10. - JP 16.09 Команда для вывода списка последних измененных файлов При поиске недавно измененных файлов, имена которых вы уже забыли, может оказаться удобной пользовательская команда /г, особенно если поиск ведется в каталоге, содержащем большое количество файлов: alias lr "Is -lagFqt \!* I head" Этот псевдоним имеет по сравнению с опцией -t (I6.02) команды Is то преимущестю, что файлы, измененные последними, появляются в начале списка. Команда head выводит только первые 10 строк списка. Простая команда lr в моем начальном каталоге выдает следующее: bermuda:home/dansmith :-) lr total 1616 -rw 1 dansmith staff 445092 Oct 7 20:11 .mush256 -rw-r—r— 1 dansmith staff 2762 Oct 7 20:11 .history drwxr-xr-x 30 dansmith staff 1024 Oct 7 12:59 text/ -rw 1 dansmith staff 201389 Oct 7 12:42 .record drwxr-xr-x 31 dansmith staff 1024 Oct 4 09:41 src/ -rw-r—r— 1 dansmith staff 4284 Oct 4 09:02 .mushrc Для сужения диапазона поиска можно также задать шаблон. Например, следующая команда выводит имена "скрытых" файлов, которые недавно были изменены: ■V 1S.0S bermudathome/dansmith :-) lr .??* -rw 1 dansmith staff -rw-r—r— 1 dansmith staff -rw 1 dansmith staff -rw-r—r— 1 dansmith staff - DS 445092 2762 201389 4284 Oct Oct Oct Oct 7 7 7 4 20; 20: 12: 09: :11 :11 :42 :02 .mush256 .history . record .mushrc Oie же эти файлы? 237
16.10 16.10 Сценарий findcmd: поиск команды В UNIX для поиска системных команд предназначены утилиты whereis (so.os) и which (sn.m). Однако утилита whereis не ищет команды в последовательности, заданной переменной PATH, поэтому она не всегда может найти сценарии в локальных системных каталогах или в вашем каталоге bin (4.02). А утилита which выводит путевое имя только первой команды с заданным именем найденной в одном из каталогов, указанных в последовательности поиска переменной PATH. Вы, как и я, по-видимому, не всегда можете с уверенностью назвать имя искомой команды. Часто приходится вспоминать, как она там называется — reference, refer или как-нибудь еще. Сценарий findcmd сокращает время подобных гаданий. Он показывает все имена команд содержащих определенную строку, во всех каталогах из последовательности поиска. Попробуем найти имена команд, содержащие строку ref: % findcmd ref /home/jerry/.bin/zrefile /usr/bin/xll/xrefresh /usr/local/bin/grefer /bin/cxref /bin/refer /usr/bin/cxref /usr/bin/refer ./preferences ^^^И Сделав пару попыток, я обычно нахожу нужную команду. Сценарий findcmd имеется на I Св1 1 компакт-диске. ^^_<4 Сначала сценарий редактирует копию переменной PATH (й.04), с тем чтобы заменить точкой findcmd (•■■•■) все записи, относящиеся к текущему каталогу. Далее двоеточие (:) в переменной IFS (35.21) позволяет интерпретатору shell разбить переменную PATHна составляющие. В цикле for (44.H) обрабатывается каждый каталог, указанный в переменной PATH, и для поиска нужных файлов запускается команда Is -l. Наконец, фильтр sed (34.24) просматривает вывод всех команд Is цикла, редактирует и выводит имена всех подходящих файлов (т.е. имена исполняемых файлов, содержащих имена нужных нам программ). - JP 16.11 Вывод "скрытых" файлов Обычно команда Is игнорирует все файлы, имена которых начинаются с точки (.). Это достаточно удобно, так как в UNIX есть множество небольших файлов конфигурации, рабочих файлов и т.п., о которых вам в большинстве случаев нет необходимости ни знать, ни беспокоиться. Но иногда такими файлами приходится заниматься очень много. Чтобы увидеть скрытые файлы, воспользуйтесь командой Is -a. Например: % cd % Is Не показывает скрытых файлов Mail mail.txt performance powertools % Is -а На этот раз выводится все .emacs Mail powertools .login mail.txt .cshrc .mailrc performance Благодаря опции -a мы видим четыре дополнительных файла: файл инициализации интерпретатора С shell, файл инициализации при входе в систему, файлы настроек для редактора GNU Emacs и почты. Мы видим также две специальные записи, . и . ., которые представляют текущий каталог и родительский каталог текущего каталога. Все каталоги в UNIX содержат эти две записи (is.o2). Для тех, кому записи . и .. не интересны, во многих UNIX-системах существует опция -А: % Is -А Выводится все, кроме . и .. .cshrc .login Mail performance .emacs .mailrc mail.txt powertools - ML 238 Часть третья Работа с файловой системе*
16.12 16.12 Полезные псевдонимы для команды Is Поскольку команда Is считается одной из самых популярных команд UNIX, имеющей многочисленные опции, то представляется логичным создание псевдонимов для организации вывода в наиболее приемлемых форматах. Многие пользователи, например, всегда желают знать все о своих скрытых файлах. И в этом есть смысл, так как данные файлы выполняют не менее важную роль, чем любые другие. В некоторых случаях их количество может уведичиваться, и они будут занимать заметный объем (например, некоторые редакторы скрывают файлы резервных копий), поэтому за такими файлами необходимо постоянно следить. Вместо того чтобы по нескольку раз набирать Is -а, вы можете создать удобный псевдоним, который будет автоматически подставлять опцию -а или -А (i6.ii): alias la "Is -aF" ИЛИ alias la "Is -AF" Здесь нужно отметить два момента. Во-первых, я рекомендую использовать la в качестве псевдонима, а не просто переименовывать команду Is. Лично мне кажется опасным сокрытие "чистой и непорочной" команды под псевдонимом. Лучше выбрать новое имя и взять за привычку использовать именно его. Если когда-нибудь вам понадобится исходная команда Is, вы сможете воспользоваться ею без каких-либо проблем. Во-вторых, что вы скажете относительно опции -F? Я специально не упоминал о ней, чтобы посмотреть, обратите ли вы на данную опцию внимание. Опция действительно полезна, и многие пользователи вставляют ее в псевдонимы команды Is. Команда Is с опцией -F показывает тип каждого файла, добавляя дополнительный символ в конец каждого имени. Возможные дополнительные символы перечислены в табл. 16.1. Таблица 16.1. Обозначения типов файлов в выводе команды Is -F Символ Отсутствует * / @ Значение Обычный файл Исполняемый файл Каталог Символическая ссылка (Ы.м) Файл типа "гнездо" (не обращайте на него внимания) Например: % 1а Псевдоним для команды Is -aF .cshrc .login Mail/ performance/ .emacs .mailrc mail.txt powertools@ Из этого следует, что Mail и performance являются каталогами, a poweiiools — символической ссылкой (команда Is -l покажет, на какой файл указывает ссылка). В этом каталоге нет ни исполняемых файлов, ни "гнезд". Можно попробовать и такую версию: alias la Is -aFC Опция -С обеспечивает вывод списка файлов в нескольких столбцах. Эта опция не нужна в системах, в которых вывод в несколько столбцов осуществляется по умолчанию (например, в SVR4). Обратите, однако, внимание, что при направлении в канал вывод команды Is состоит из одного столбца, если только не используется опция -С. Чтобы сохранить вывод в несколько столбцов, применяйте команды типа Is -С | more. И наконец, если вам часто требуется полный список, используйте следующий псевдоним: alias 11 Is -l Этот псевдоним, на первый взгляд, несущественно сокращает набор, но только если вы не вводите его десятки раз подряд. Кроме того, его легко запомнить как аббревиатуру от long listing (длинный список). Некоторые UNIX-системы даже содержат команду // в качестве стандартной. - DG, ML Где же эти файпы? 239
16.13 16.13 Файл недоступен? Возможно, имя набрано с пробелами Что здесь неправильно? % Is afile exefiles j toobig % lpr afile lpr: afile: No such file or directory Как же так? Ведь команда Is показывает, что файл существует. Попробуйте следующее: % Is -1 | cat -v -t -e total 89$ -rw-rw-rw- 1 jerry -rw-r--r-- 1 root -rw-rw-rw- 1 jerry -rw-r—r— 1 root -v 25.07 -t -e 25.06 28 Mar 7 19:46 afile $ 25179 Mar 4 20:34 exefiles$ 794 Mar 7 14:23 j$ 100 Mar 5 18:24 toobig$ Команда cat -e обозначает конец строки символом $. Обратите внимание, что для файла afile знак доллара выводится с отступом на одну позицию. Вот в чем дело: имя файла закачивается пробелом. С пробельным символом [TAB] возникают те же проблемы ,-хотя если вывод направляется на терминал, то команда Is -g об.н) (во многих версиях UNIX), представляет его как ?. Чтобы переименовать файл afile и дать ему имя без пробела, введите % mv "afile " afile Кавычки (8.Ы) заставляют интерпретатор shell включить пробел как часть первого аргумента, который передается команде mv. Точно так же кавычки действуют при выполнении других команд UNIX, например rm. - JP 16.14 Вывод непечатаемых символов в именах файлов Время от времени нам попадаются имена файлов с непечатаемыми символами, пробелами и прочим "мусором". Обычно это является результатом какой-то ошибки, однако к возникновению проблем не приводит. При использовании BSD UNIX команда Is может предоставить вам некоторую помощь. Она превращает непечатаемые символы в знаки вопроса (?), сигнализирующие о том, что в этом месте есть что-то "интересное".* Например: % Is ab??cd Отсюда видно, что в имени между ab и cd есть два непечатаемых символа. Чтобы удалить или переименовать этот файл, необходимо использовать шаблон с метасимюлами, например ab??cd. Будучи еще новичком в UNIX, я создал множество файлов с несколько странными именами. Команда Is сообщила мне, что все они начинаются с ????, и я по наивности набрал rm ????*. И тут начались мои неприятности... Более подробно об этой печальной истории рассказывается в параграфе 23.02. На устранение нежелательных последствий были потрачены следующие день и ночь. Отсюда вывод: вы должны взять за правило проверять, какие имена файлов соответствуют шаблону, с помощью команды echo. Опция -q используется по умолчанию и только при условии, что.устройством стандартного вывода команды Л является терминал. Если вывод передается в канал или перенаправляется в файл, не забудьте добавить -q. 240 Часть третья. Работа с файловой системой
16.16 При использовании System V UNIX вы, вероятнее всего, столкнетесь с рядом других проблем. В этой системе команда Is не превращает непечатаемые символы в вопросительные знаки. А точнее говоря, она просто отправляет эти необычные символы на терминал, который может отреагировать на них самым непредвиденным образом. Большинство непечатаемых символов имеют специальное назначение — or "прекратить прием данных" до "очистить экран". Чтобы этого избежать, используйте опцию -b* Данная опция заставляет команду Is выводить восьмеричное значение каждого непечатаемого символа с обратной косой чертой впереди. Например, в System V мы получим: % Is -b ab\013\014cd Как видите, непечатаемые символы имеют восьмеричные значения 13 и 14 соответственно. Согласно таблице кодов ASCII (Si.oj), эти значения соответствуют комбинациям клавиш [CTRL-k] и [CTRL-1]. И если вы хотите узнать, что произойдет, то представьте, что [CTRL-1] является командой перевода страницы, которая заставляет многие терминалы очистить экран. Вот почему стандартная команда Is может вести себя столь странно. Понимая, о чем идет речь, можно воспользоваться шаблоном со специальными символами, с тем чтобы удалить или переименовать данный файл. - ML 16.15 Веселый сценарий для новообращенных в UNIX Мне пришлось работать в разнообразных операционных системах: UNIX, VMS, MS-DOS и прочих. Некоторые пользователи применяют команду dir для выполнения того, что делает команда Is. Я же написал сценарий, который, во-первых, уменьшает объем вводимой информации, а во-вторых, повышает мне настроение. % dir Hey! This is UNIX! Well, okay... but just this once... total 265 -rw-rw-r— 1 ellie 47279 Dec 16 13:22 2edit.2 -rw-r-r- 1 jerry 21802 Nov 12 18:24 7911.ps drwxrwsr-x 2 jerry 14848 Dec 24 07:17 RCS Сценарий hey для интерпретатора Bourne shell довольно прост. Он выводит свое сообщение в стандартный поток ошибок, поэтому оно не будет переадресовано в файл или канал. Затем сценарий проверяет имя, по которому он был вызван (в данном случае dir), и запускает команду, заданную для выполнения вместо введенной (здесь — Is -/): case "$0" in -yer 44.15 *dir) ls-1 ${l + "$@") ;; *md) mkdir ${l+"$@"} . . .H Т.Д. . . esac Одному файлу сценария hey можно дать сколько угодно имен путем создания ссылок на него (is.03). В параграфе 8.08 подобная конструкция приведена для другой цели. - JP 16.16 Автоматическое создание уникальных имен файлов В процессе работы со сценариями, псевдонимами и другими программами могут понадобиться временные файлы, где обычно хранятся данные, которые будут использоваться позже. Если программа будет выполняться более одного раза или если временный файл должен остаться после выполнения программы, понадобится разработать способ создания уникального имени файла. В системе BSD передайте вывод команды Is -q по каналу команде cat-vumnd-c (2s.ht), и вы увидите, какие именно непечатаемые символы здесь присутствуют. 0|» же эти фейпы? 241
16.17 Один из способов создания такого имени заключается в использовании идентификатора-.:..] процесса (жоз), содержащегося в переменной $$, и часто применяется при создании файлорв ' каталоге /tmp pi.av. Файл можно было бы назвать /tmp/MYPROGSS; интерпретатор shell превратив ' это имя в нечто наподобие /tmp/MYPROG1234 или /tmp/MYPROG2847I. Если для работы вашей ' программы нужно более одного временного файла, добавьте к именам файлов еще один символ: errs=/tmp/MYPROGe$$Routput=/tmp/MYPROGo$$ Помните о возможном ограничении в некоторых старых версиях UNIX длины имени файлов 14 символами. Параметр Добычно добавляет от 2 до 5 символов. Если в вашей системе UNIX нет команды date, использующей для изменения формата ее вывода параметр +, инсталлируйте эту команду (51.ю). В частности, вы можете объединить вывод значений, определяющих месяц, число, время (часы, минуты и секунды): % date Thu May 30 07:21:13 EDT 1991 % date +'%m%d%H%M%S' 0530072124 Для создания временного файла, именуемого в соответствии с текущей датой и (или) временем, используйте ключевой символ + и обратные кавычки (' ') (9.щ. Например, 31 мая команда, приведенная ниже, сохранит в переменной temp значение foo.0531, a 7 декабря— - значение foo.1207. temp=foo."date +'%m%d'' В параграфе 21.03 описана другая система именования временных файлов. - JP 16.17 Получение имени каталога из путевого имени файла Иногда при написании сценариев или функций бывает нужным определить по полному путевому имени имя родительского каталога. (Зная имя родительского каталога, можно, в частности, выяснить, имеете ли вы право на запись в данный каталог, т.е. можете ли удалять и переименовывать файлы.) Если путевое имя содержится в переменной интерпретатора csh (не в переменной среды), используйте модификатор :h (9.м>. В интерпретаторе Bourne shell проверьте, есть ли в вашей системе команда dimame (4S.1S). В случае отсутствия таковой возьмите ее GNU-версию с компакт-диска или же воспользуйтесь командой ехрг (45.28) с регулярным выражением (2б.щ которая выдает путь к файлу без последней косой черты. Например, если путевое имя /home/mktg/fred/afile содержится в переменной/?/е, то следующие команды для интерпретаторов csh и sh запишут строку /home/mktg/fred в переменную dir. % set dir=$file:h $ dir*'dimame "$file"' $ dir»'expr "$file" : ■\(.*\)/'" Для обработки различных путевых имен используйте следующее регулярное выражение в команде sed_ (H.24y. % ... sed 's@/[A/]*$@@' ... - J? 16.18 Получение списка файлов, созданных или отредактированных сегодня Если ваш каталог переполнен файлами, а вы пытаетесь выяснить, какие файлы были изменены (и созданы) сегодня, то вот как это можно сделать.* Создайте сценарий, который будет * Команда find с оператором -mtime -1 (п.чт) выводит список файлов, модифицированных в течение последних 24 часов. А это не совсем то, что нужно. 242 Часть третья. Работа с файловой системой
16.19 ls__today вычислять сегодняшнюю дату. Передайте по каналу вывод команды Is -l утилите awk. В сценарии утилиты awk укажите название месяца (второе слово в выводе команды date) в строковой переменной т. Поместите значение, соответствующее числу месяца, в целочисленную переменную d сценария. Целочисленную переменную нужно использовать для того, чтобы такая выходная дата команды date, как Jun 04, совпадала с форматом вывода даты команды Is — Jun 4. Выведите все строки, в которых обе даты совпадают. #!/bin/sh set 'date' Is -1 | awk "BEGIN { m = \"$2\"; d = $3 } \$5 == m SS \$6 == d {print}" Если ваша версия команды Is -I выдает как имя владельца файла, так и имя группы пользователей, замените параметр $5 параметром $6, а параметр $6 — параметром $7. Вы можете облегчить себе жизнь, если инсталлируете команду sis (16.29), позволяющую точно устанавливать формат вывода (в том числе формат даты). - JP 1S.19 Сценарий stree: простое дерево каталогов Приведем простой сценарий, отображающий дерево каталогов. Его можно вывести на любой терминал, напечатать, отправить в почтовом сообщении и т.д. Если имя каталога сценарию не передается, то он обрабатывает текущий каталог. При передаче ему опции -a (all — все) перечисляются все файлы, каталоги, символические ссылки и т.д. Иначе перечисляются только каталоги. Например: % stree Tree lib И II м Ч II н fo: lib г directory lib at cron ii n csh ksh RCS.Z tmac RCS test RCS RCS Каталог высшего уровня помещен у левого края. Первый уровень каталогов смещен на одну колонку. Кавычка (") под именем означает тот же родительский каталог, что и выше. Поэтому последний каталог в приведенном списке имеет имя lib/tmac/ms/RCS. Вот текст сценария: #! /bin/sh case "$1" in -a) shift t{l-.\ 45.12 dir=${l-.} # По умолчанию — текущий каталог echo Tree for directory $dir and its files: *) findtype="-type d" # Если нет опции -а, то выполняется # команда find USE "-type d" dir=$[l-.} echo Tree for directory $dir: Где щр эти файпы? 243
16.20 esac "новая строка 8.14 echo " $dir" find $dir $findtype -print | tr / W001 | sort -f | tr \\001 / I &.U.07 sed -e s@\A$dir@@ -e /\A$/d -e 'з@[л/]*/@ 'fTAB]@q' Команда tr (.is.ii) в сценарии использована с целью замены, во время сортировки символов, косой черты (/) комбинацией клавиш [CTRL-a] (восьмеричный код 001 (sr.oj)). Это приводит к тому, что косая черта при сортировке идет перед буквами, поэтому имена каталогов всегда указываются впереди списка их содержимого. - JP Программа vtree: визуализация дерева каталогов Программа vtree по своему назначению подобна сценарию stree (16.19), но имеет несколько дополнительных возможностей, заслуживающих особого внимания. Эту профамму можно использовать для получения информации не только о каталогах, но и о том, сколько дискового пространства они занимают: % vtree /usr /usr : 0 lost+found : 0 local : 13880 lost+found : 0 bin : 23673 RCS : 41 lib : 6421 news : 28896 bin : 2 newsbin : 269 batch : 97 expire : 138 input : 91 Профамма vtree имеет опции, позволяющие узнать, сколько индексных дескрипторов (1.22) использовано в каждом каталоге, а также управлять количеством отображаемых нижестоящих уровней. Опция -v заставляет профамму выводить "визуальное" представление дерева, отображающее структуру каталогов: % vtree -v /usr /usr +-> lost+found +-> local +-> lost+found +-> bin +-> RCS +-> lib +-> news +-> bin | +-> newsbin +-> batch I I +-> expire I I +-> input - LM 16.21 Поиск всех каталогов с одинаковыми именами Я собрал много профамм. Один из дисков у меня заполнен общедоступными профаммами. Некоторые каталоги представляют собой "коллекции" наподобие магнитных лент для фупп пользователей системы Sun. Очень может быть, что одна и та же программа находится в различных каталогах. Чтобы предотвратить подобную нерациональную трату дискового пространства, я создал указатель каталогов и путей доступа к ним. Если существует два 244 Часть третья. Работа с файловой системой
16.22 каталога с одинаковыми именами, то я хотел бы знать об этом. Возможно, один из каталогов следует удалить. Лишние каталоги очень просто обнаружить с помощью следующих команд: find . -type d -print | \ awk -F/ '{printf("%s\t%s\n",$NF,$0);}' | \ sort [Вы можете для этих команд определить псевдонимы или функции (ю.ог). — JP\ Команда find (izoi) выводит список всех каталогов. Команда awk (зз.пу использует косую черту как разделитель полей. Переменная NF содержит информацию о количестве полей, и ее значение $NF соответствует последнему полю. $0 является переменной команды awk и содержит всю строку. Из результата выполнения этих команд можно определить, где расположены все каталоги misc. misc ./Xll/lib/Xll/fonts/misc misc ./misc misc ./src/XView2/contrib/examples/misc misc ./src/XView2/fonts/bdf/misc misc ./src/XView2/lib/libxvin/misc misc ./src/XView2/lib/libxvol/misc misc ./src/XView2/misc Набор этих команд можно преобразовать в сценарий, принимающий аргументы. Если никакой аргумент не указан, то по умолчанию в качестве такового выбирается точка (.). #!/bin/sh drjeth # Использование: dir_path [искомый каталог ...] $)•-.) 45.12 find ${*-.) -type d -print | awk -F/ { printf ("%s\t%s\n",$NF,$0); }' I sort [Эту прекрасную идею можно использовать и для поиска файлов-двойников. Замените -type d аргументом -type f. Если вы (или все пользователи вашей системы) захотите часто задействовать этот сценарий, запускайте его ночью с помощью программы сгоп (Ш2) или а[ (40.Ы). Вывод сохраните в файле. Для проведения поиска в этом файле используйте скоростную команду look рл.щ. В параграфе 17.19 описана другая база данных для команды find. - ПК, JF\ - ВВ 16.22 Сравнение структуры двух каталогов Предположим, у вас есть исходный каталог. Вы копируете содержащиеся в нем файлы в другой каталог, редактируете некоторые из них и добавляете новые. Впоследствии вам, возможно, понадобится определить разницу между каталогами. Если системная команда diff (2s.oi) имеет опцию -г (рекурсивное выполнение), рекомендуем ею воспользоваться. В System V есть команда dircmp. Вывод этой команды форматируется командой рг (43.от). В результате вы получите страницы длиной 66 строк с заголовками: % dircnp а Ь Sep 16 09:26 1991 a only and b only Pagel ./foo.tmp ./defs.h Sep 16 09:26 1991 Comparison of a b Pagel directory same ./Makefile directory .data same ./data/testl Где же эти файлы? 245
16.23 different same /data/test2 /pqp.help /pqs.help В списке a only and b only в первом столбце приведены файлы, содержащиеся только в первом каталоге, а во втором столбце — файлы, содержащиеся только во втором каталоге, В списке Comparison of a b сравниваются файлы, имеющиеся в обоих каталогах. Сравнение производится рекурсивно: если есть подкаталоги, то команда dircmp проверяет и их. Команда dircmp -s останавливает вывод сообщений об идентичности файлов. При необходимости запустить для различающихся файлов команду diff используйте опцию -d. Следующая команда dircmp выводит новую страницу для каждой запускаемой ею команды diff. % dircmp -d -s a b Sep 16 09:35 1991 a only and b only Pagel Sep 16 09:35 1991 Comparison of a b Pagel Sep 16 09:35 1991 diff of .pqp.help in a and b Page 1 3c3,4 < -# "Only this printer"... 'pqp -31 would print on #3. j > -# "Only this printer"... 'pqp -3' would print only on #3; ' > other printer queues will be held. Разработчики предполагали, что вы будете направлять вывод на принтер. Я обьгчно читаю результаты с экрана при помощи программы постраничного вывода less -s fts.M), которая обеспечивает удаление многочисленных пустых строк. Если команды less и тоге не поддерживают опцию -s, попробуйте передать вывод по каналу команде cat -s (2s.io) или фильтру sed (34.IS). - JP 16.23 Сравнение имен файлов в двух каталогах Допустим, у вас есть два каталога с большим количестюм подкаталогов и файлов и вы хотите выяснить, какие файлы находятся только в одном каталоге, а какие — только в другом. Если у вас нет команды dircmp (16.22), предлагаем быструю, но несколько громоздкую замену. Пронумерованные приглашения (1.ю> типа 3% предназначены только для объяснения. 1% cd каталог! 2% find . -type f -print | sort >/trap/dirl 3% cd каталог2 4% find . -type f -print | sort >/tmp/dir2 Г...1 15.02 5% comm -3 /tmp/dir [12] 6% rm /tmp/dir[12] Команда comm (28.12) выводит два столбца: в левом представлены файлы только из каталога1, а в правом — только из каталога2. Можно также получить другую информацию, например список файлов, хранящихся в обоих каталогах. Этот прием прекрасно работает и в случае каталогов из других компьютеров. Запустите команду find I sort на удаленном компьютере. Передайте полученный файл в компьютер с другим деревом каталогов и запустите там команду comm. Команду diff можно также выполнить по сети, заменив команды 3-5 (см. выше) следующими: reh 1.32 % rsh сервер \ 'cd каталог2; find . -type f -print | sort | \ diff -e /tmp/dirl - 246 Часть третья. Работа с файловой системой
26.25 Аргумент -е заставляет команду dijf читать данные со стандартного ввода (из удаленной команды find). В параграфе 13.13 описан аналогичный прием для сетевой файловойсистемы. Параграфы 16.19 и 16.20 посвящены программам, помогающим просматривать дерево каталогов. - JP 16.24 Подсчет файлов по типам Я часто работаю с утилитой awk (jj.ii). Одной из самых моих любимых особенностей этой программы является возможность использовать ассоциативные массивы. В таких массивах в качестве индексов можно задействовать любые элементы. В следующем примере для подсчета количества файлов того или иного типа я воспользуюсь выводом команды file ps.os) как индексом массива. #!/bin/sh # Использование: count_types [каталог ...] # Подсчитывает количество файлов каждого типа # Первоначальная версия - Bruce Barnett # Обновленная версия — yu@math.duke.edu (Yunliang Yu) %{*-) 45.12 find ${*-.( -type f -print I xargs file | xargs 9.21 awk . ( $1=NULL; t[$0]++; ) END { for (i in t) printf("%d\t%s\n", t[i], i); ) ' I sort -nr # Сортировка результатов по количеству файлов каждого типа # (в порядке убывания) Вывод этого сценария может иметь следующий вид: 38 ascii text 32 English text 20 С program text 17 spare executable not stripped 12 compressed data block compressed 16 bits 8 executable shell script 1 spare demand paged dynamically linked executable 1 executable /bin/make script - BB 16.25 Получение списка файлов в соответствии с датой последней модификации и размером Прежде чем удалять большой каталог, нужно определить, нет ли в нем файлов, которые еще могут пригодиться. Предлагаем вашему вниманию сценарий, выводящий сводку размеров файлов, упорядоченную по времени их последней модификации. Как вы, возможно, помните, команда Is -l выводит месяц, число и время (часы и минуты), если файлу менее шести месяцев, и показывает месяц, день и год, если файлу более шести месяцев. Используя это ее свойство, сценарий создает сводку по каждому из шести последних месяцев и по каждому году для более старых файлов. W ^ #!/bin/sh 1 ® 1 * Использование: age_files (каталог ...] |^_^ # Выводит информацию о размерах файлов, упорядоченных по времени их модификации Wjiles # # Укажите, какую версию команды Is вы используете # System V Где же эти файлы? 247
16.26 #LS="ls-ls" # Berkeley LS="ls-lsg" # $(*:-.| 45.12 find ${*:-.} -type f -print I xargs $LS | awk ' xaijs 9.21 j аргумент 7 — месяц; аргумент 9 — либо время в формате hh:mm, # либо год в формате уууу # проверка формата аргумента: hh:mm или уууу < if (split ($9, junk,":") ( sz[$9]+=$l; ) else ( sz[$7]+=$l; ) ) END ( ■ for (i in sz) prinf("%d\t%s\n", sz[i], i); }' I sort -nr Этот сценарий может генерировать следующий результат: 5715 1991 3434 1992 2929 1989 1738 Dec 1495 1990 1227 Jan 1119 Nov 953 Oot 61 Aug 40 Sep - BB 16.26 Поиск текстовых файлов с помощью сценария findtext Некоторые из моих каталогов, например bin (4.02), наряду с нетекстовыми файлами (исполняемыми двоичными, сжатыми, архивами и т.д.) содержат и текстовые файлы (например, сценарии, документацию). При попытке найти определенный файл с помощью команды grep (27.00 или' программы постраничного вывода (25.оз, 25.04) проверка нетекстовых файлов будет вызывать на экране появление "мусора"; Я бы хотел иметь способ дать команду: "Искать только файлы, содержащие текст". Такую возможность нам предоставляет сценарий Jintext. Он запускает команду file (25м), чтобы определить тип каждого файла,.и выводит имена только, текстовых файлов. Так, я не стану набирать % egrep им» * а введу '...' 9.16 % egrep им» findtext *' Вот текст сценария, а затем мы погоюрим о том, как он может быть установлен в вашей системе. #!/bin/sh да . # Передает вывод утилиты file программе sed, чтобы вывести findtext # имена нужных файлов. Обратите внимание: # разные версии утилиты file выводят различные сообщения'. 248 Часть третья. Работа с файловой системой
16.26 # Проверьте свою систему с помощью команды strings /usr/bin/file # или cat /etc/magic и соответствующим образом подправьте текст сценария, /usr/bin/file "$@" | sed -n ' /MMDF mailbox/b print /Interleaf ASCII document/b print /PostScript document/b print /Frame Maker MIF file/b print /c program text/b print /fortran program text/b print /assembler program text/b print /shell script/b print /c-shell script/b print /shell command/b print /c-shell command/b print /English text/b print /ASCII text/b print /\[nt\]roff, tbl, or egn input text/b print /executable .* script/b print b .; :print s/:lfa51.*//p' Сценарий прост: он запускает утилиту file с аргументом, указанным в командной строке. Вывод утилиты file выглядит следующим образом: C0PY2PC: directory Ех24348: empty FROM_consult.tar.Z: compressed data block compressed 16 bits GET_THIS: ascii text hmo: English text msg: English text 1991.ok: [nt]roff, tbl, or egn input text Вывод направляется по каналу в фильтр sed (34.24), который отбирает строки, по всем признакам принадлежащие текстовым файлам. Фильтр отбрасывает все символы после имени файла (включая двоеточие). Разные версии утилиты file будут выводить различную информацию. Например, некоторые версии читают файл /etc/magic. Чтобы определить, файлы какого типа ваша утилита file считает текстовыми, воспользуйтесь следующими командами: % strings /usr/bin/fila > possible % cat /ate/magic » possible % vi possible Файл possible будет содержать список описаний, которые команда strings нашла в двоичном файле утилиты file (некоторые из них предназначены для текстовых файлов). Если в вашей системе есть файл /etc/magic, то он будет содержать примерно такие строки: 0 0 0 0 long string string string 0x1010101 <!OPS %! <MIFFile MMDF maibox Internal ASCII document PostScript document Frame Maker MIF file Сохраните описания текстовых файлов, содержащиеся в правом столбце. Затем превратите каждую строку отредактированного файла possible в команду редактора sed: Ъртл34.19 /описание/Ъ print Следите за специальными символами в описаниях, полученных с помощью утилиты file. Мне пришлось обработать в двух последних строках сценария два особых случая. Где же эти файлы? 243
16.27 • Во-первых, в выражении для фильтра sed необходимо было заменить строку executable %s script, полученную от утилиты file, строкой /executable . * script/b print. Такая замена была обусловлена тем, что наша утилита file заменяет %s именем типа /bin/ksh. • Во-вторых, назначение символов, которые редактор sed рассматривает в качестве регулярных выражений (например, квадратные скобки в выражении [nt]roff), необходимо было изменить при помощи обратной косой черты. В сценарии я использовал выражение \[nt\]troff. При помощи языка Perl (37.01) можно создать более простую версию этого сценария, поскольку данный язык имеет встроенные средства проверки того, является ли файл текстовым. Текстовый файл опознается путем проверки одного-двух первых блоков на наличие необычных управляющих кодов и метасимволов. Если таковых слишком много (более 10 %), значит, это не текстовый файл. Perl-сценарий невозможно настроить, скажем, на пропуск некоторых типов файлов, однако данная версия намного проще! Вот как она выглядит: % perl -le '-Т ££ print while $_ = shift1 * Если вы захотите вставить эту строку в определение псевдонима ао.02), то проблемы, связанные (щ) J с защитой специальных символов (47.02, s.isj, в интерпретаторе С shell могут усложнить дело. Но команда makealias poos) позволяет получить следующее определение псевдонима команды, cshjnit, выполняющей поставленную задачу: alias findtext 'perl -le 'V'-T 4S print while $_ = shift'V *' - JP sh init 16.27 Команда newer: вывод списка самых новых файлов Приведем определение короткой команды, которая устанавливает, какие файлы в группе являются самыми новыми. -616.08 alias newer "Is -dt \\* | head -1" ЕЕсли в вашей системе нет команды head (25.20), используйте вместо нее команду sed Iq. Предположим, у вас есть два файла, с именами plan.vl и plan.v2. He знаю, как вы, а я частенько по ошибке редактирую неправильную версию, а спустя пару часов не могу «***- вспомнить, какие же изменения были внесены. С помощью данной команды можно выяснить, - какой файл изменялся последним: * 15.02 % newer plan.v* plan.vl Вот и приехали! Я редактировал не ту версию файла. (Отредактировать самую новую версию можно с помощью команды % emace 'newer plan.v*' используя обратные кавычки.) - ML 16.28 Сценарий oldlinks: проверка целостности символических ссылок Использование символических ссылок осложняется тем обстоятельством, что они довольно "хрупкие" (18.Щ. Ссылка и файл — это различные по своей сути элементы. Ссылка всего лишь содержит имя настоящего файла. Следовательно, если файл будет удален или переименован, то у вас останется "мертвая", или "старая", ссылка, т.е. ссылка, указывающая на несуществующий файл. 250 Часть третья. Работа с файловой системен
16.29 Это постоянно запутывает, особенно начинающих пользователей. Можно, например, столкнуться со следующим: % is nolink nolink % cat nolink cat: nolink: No such file or directory Файл явно присутствует, но команда cat "утверждает", что его не существует. Избежать возможных проблем вам поможет сценарий, который проверяет целостность ссылок. Приведем один из таких сценариев, написанных Томом Кристиансеном. В нем для выявления всех ссылок используется команда find, а для вывода имен ссылок, указывающих на несуществующие файлы, предназначен рег/-сценарий (jzoi). #!/bin/sh find . -type 1 -print I perl -nle '-e I I print' Сценарий только перечисляет "мертвые" ссылки, не пытаясь их удалить или сделать что-нибудь радикальное. Если вы хотите произвести другие действия (например, автоматически удалить подобные ссылки), воспользуйтесь результатом выполнения сценария, указав его имя между обратными кавычками (9.16). Например: % гш "oldlinks' - ML 16.29 Команда sis: супер-команда Is с возможностью выбора формата Команда Is -I и родственные ей команды, такие, например, как stat pi.U), выдают много информации о файле (точнее, об индексном дескрипторе (1.22)). Эта информация предоставляется в таком виде, чтобы ее можно было легко воспринимать. Однако формат вывода не всегда соответствует желаемому. Это может создать трудности для программистов: разбор и анализ результатов с помощью программ sed, av/k и некоторых других — занятие довольно сложное (см. параграф 16.25). Кроме того, вывод команды Is -I в BSD и System V имеет различный вид. Команда sis наряду с рассмотренными выполняет и множество других задач. В частности, она позволяет: • создавать собственный выходной формат — отбирать необходимую информацию и представлять ее в требуемом виде; • сортировать вывод по одному или нескольким полям; • выводить дату в развернутом виде, числовом или словесном (с учетом, при желании, секунд). Особенно радует тот факт, что формат даты не меняется для файлов, которым более шести месяцев (если только не используется опция -и). Приемлемые форматы вывода команды sis подробно описаны на соответствующей странице интерактивного руководства, имеющегося на компакт-диске. Приведем несколько примеров. Начнем с вывода обычной команды Is -l. (Команда sis -l по умолчанию выводит дату в стандартном формате, вне зависимости от времени существования файлов, причем без строки total, составляющей еще одну проблему для программистов.) % is -1 total 3 -rw-r 1 jerry 1641 Feb 29 1992 afile lrwxrwxrwx 1 jerry 8 Nov 18 00:38 bfile -> ../bfile Ниже приведен формат, более приемлемый для пользователей, которые не являются фанатиками UNIX (может оказаться целесообразным определение этого формата с помощью псевдонима или функции интерпретатора shell (Ю.оо). Сначала выводятся значения даты и гЛе же эти файлы? 251
16.29 времени, затем — имя полюователя и размер файла в килобайтах, а затем — имя файла без информации о символической ссылке вида -> . . /bf ile: % sis -p '%m"%F %d, 19%y %r" %u %4skK %n' February 29, 1992 03:43:00 PM jerry 2K afile November 18, 1992 00:38:22 AM jerry IK bfile Кроме того, можно воспользоваться простым форматом вывода, в котором представлены все три временные характеристики файла общ: даты модификации,, доступа и изменения индексного дескриптора. Сначала, воспользовавшись командой echo, выводим заголовок: % echo 'modify access inode1; \ sis -p '%m"%D" %a"%D" %c"%D" %n' modify acsess inode 02/29/92 09/17/92 11/18/92 afile 11/18/92 11/18/92 11/18/92 bfile Теперь "попросим" команду sis сгенерировать набор команд UNIX, которые можно использовать, например, при работе с shell-архивом (ющ. Эти команды предназначены для восстановления прав доступа, даты и имени владельца (UID) файла при извлечении его из архива. touch 21.07 % sis -p 'chmod %P %n; chown %U %n; touch %m"%m%d%H%M%y" %n* chmod 640 afile; chown 225 afile; touch 0229154392 afile chmod 777 bfile; chown 225 bfile; touch 1118003892 bfile Я не рассказал вам об опциях сортировки, равно как и о многих других опциях форматирования вывода. Но все же надеюсь, что некоторые из наиболее интересных идей я до вас донес. - JP
17 Команда find: поиск файлов -name имя_файла -perm режим 17.01 Команда find великолепна. Нужно только научиться пользоваться ею Команда find является одной из самых полезных и важных утилит UNIX. Она находит файлы, удовлетворяющие заданному набору параметров, начиная с имени файла и заканчивая датой его модификации. В данной главе мы постараемся рассмотреть достаточно широкий круг вопросов. В качестве введения даем краткую сводку операторов команды % find луг* оператора где путь соответствует каталогу, в котором команда find начнет поиск, а операторы (мы, правда, больше привыкли к термину опции) сообщают команде, какие файлы вас интересуют. Все существующие операторы перечислены ниже. Поиск файлов с заданным именем. Этот оператор используется чаще всего. Выражение имя_файла может содержать метасимволы (15.02), которые нужно защищать кавычками, с тем чтобы предотвратить их обработку интерпретатором (см. параграф 17.04). Поиск файлов с заданным режимом доступа (22.02). Режим доступа нужно задавать, используя восьмеричный код (1.23) (см. параграфы 17.10 и 17.15). Поиск файлов, которые имеют тип, заданный параметром с. Этот параметр является однобуквенным кодом. Например, f — обычный файл, b — специальный блочный файл, 1 — символическая ссылка и т.д. (см. параграф 17.13). Поиск файлов, принадлежащих указанному владельцу. Параметр владелец может быть также идентификатором пользователя (зг.оз) (см. параграф 17.16). Поиск файлов, принадлежащих указанной группе пользователей. Параметр группа может быть также идентификатором группы (зв'оз) (см. параграф 17.16). Поиск файлов длиной л блоков. Блок равен 512 байтам. Запись +л означает поиск файлов с размером, превышающим л блоков. Запись пс означает поиск файлов с размером в л символов. Думаю, вы сами догадаетесь, что означает запись +пс (см. параграф 17.14). Поиск файлов с индексным дескриптором номер л (1.22) (см. параграф 17.10). -type с -user владелец -group группа -mum л Команда find: поиск фатов 253
-atime л -mtime л -ctime л -newer имя файла Поиск файлов, доступ к которым осуществлялся л дней тому назад. Запись +л означает поиск файлов с датой доступа больше л дней назад, т.е. таких, к которым последние л дней не было доступа. Запись -л означает поиск файлов с датой доступа менее л дней назад, т.е. таких, доступ к которым состоялся в последние л дней (см. параграф 17.07). Аналогичен оператору -atime, с той лишь разницей, что проверяется время последней модификации содержимого файла (см. параграфы 17.05 и 17.07). Аналогичен оператору -atime, с той лишь разницей, что проверяется время последнего изменения индексного дескриптора (1.22). "Изменение" означает, что либо файл был модифицирован, либо был изменен один из его атрибутов, например имя владельца (см. параграфы 17.05 и 17.07). Поиск файлов, модифицированных позже, чем заданный файл (см. параграфы 17.08 и 17.09). Часто возникает необходимость в поиске файлов, отвечающих сразу нескольким критериям. Поэтому нам нужен механизм объединения нескольких операторов. оператор1 -а оператор2 оператор! -о оператор2 ! оператор \( выражение \) Поиск файлов, соответствующих как оператору!, так и операто- ру2. Оператор -а не обязателен. Если два критерия поиска указаны рядом, то команда find предполагает, что вас интересуют файлы, удовлетворяющие им обоим (см. параграф 17.12). Поиск файлов, соответствующих либо оператору!, либо оператору 2 (см. параграф 17.06). Поиск всех файлов, не соответствующих заданному оператору. Оператор ! обозначает логическую операцию НЕ (см. параграф 17.06). Изменение порядка выполнения операторов. В сложных выражениях прежде всего определяется значение выражения (см. параграф 17.06). Следующая группа операторов сообщает команде find, какие действия нужно совершать над найденными файлами. -print -exec команда -ok команда Вывод имени файла в стандартный поток вывода (см. параграфы 17.02 и 17.03). Выполнение указанной команды. Чтобы использовать в команде путевое имя файла, который только что был найден, примените специальный символ {}. Параметр команда должен заканчиваться обратной косой чертой и точкой с запятой (\;). Например, строка % find -name "*.o" -exec rm -f {} \; заставляет команду find удалить все файлы, заканчивающиеся на .о (см. параграф 17.10). То же самое, что -exec команда, но в отличие от последней спрашивает вашего разрешение на выполнение указанной команды. Эта опция полезна для тестирования команды find (см. параграф 17.10). К сказанному нужно добавить, что команда find является тем инструментом, которым разработчики очень любят манипулировать, добавляя или удаляя некоторые операторы. Перечисленные выше операторы действительны в любой системе. Просмотрев свое системное руководство, вы можете обнаружить ряд других операторов. - ML Часть третья. Работа с файловой системой
17.02 17.02 Перемещение по большому дереву каталогов Главное и наиболее очевидное применение команды find — поиск старых, больших или неиспользуемых файлов, местонахождение которых вы не помните. Однако самой важной характеристикой команды find, наверное, является ее способность перемешаться по подкаталогам. Обычно интерпретатор shell предоставляет команде список аргументов. Это значит, что UNIX-программы чаще всего получают в качестве аргументов только имена файлов, без имен каталогов. Лишь для некоторых из программ можно указать имя каталога — в таком случае они станут обрабатывать и подкаталоги. Сказанное относится в первую очередь к командам find, tar, du и diff. То же самое можно сказать и о некоторых версиях команд chmod, chgrp, Is, rm и ср, но только при условии, что указана опция -г или -R. Как правило, большинство команд не работают со структурой каталогов и при замене метасимволов (ism) в именах каталогов полагаются только на интерпретатор shell. Так, чтобы удалить из группы каталогов все файлы с именами, заканчивающимися на . о, необходимо ввести следующее: % rm *.o */*.о */*/*.о Однако набрать такую строку достаточно трудно, а кроме того, вы не можете быть уверенными, что найдены все файлы. С одной стороны, интерпретатор shell не видит скрытых файлов, а с другой, даже если файлы соответствуют шаблону */*/*/*.о, они не всегда будут удалены. Еще одна проблема, связанная с выполнением этой команды, — получение сообщения об ошибке Arguments too long (слишком длинные аргументы). Его появление означает, что интерпретатор shell подставил слишком много аргументов, соответствующих введенному шаблону. Команда find решает все описанные проблемы. Приведем пример использования команды find для вывода всех имен файлов каталога и всех имен подкаталогов. Это делается с помощью простой команды % find . -print Первым аргументом команды find являются путевые имена каталогов. В данном случае точка (.) служит именем текущего каталога (i.2i). Аргументы, идущие после путевых имен, всегда начинаются знаком минуса (-) и сообщают команде find, что делать с найденным файлом. Это операторы поиска. В нашем примере просто выводятся путевые имена. В интерпретаторе С shell тильду (~) (i4.ii) можно использовать так же, как и конкретные пути. Например: % find - -barnett /usr/local -print При желании вы можете набрать % find / -print и вывести список всех файлов в системе. [Эта команда допустима только на однопользовательских рабочих станциях с собственными дисками. Она способна так загрузить диски работой, что пользователи могут подумать о вторжении в систему злоумышленника. Если вам действительно нужен подобный перечень файлов и в вашей системе имеется быстрая версия команды find (n.is), целесообразнее использовать команду find '/*'• — JP\ Команда find направляет свой вывод в стандартный поток вывода, поэтому полученный список имен файлов можно передать другим командам. Это можно сделать, в частности, с помощью механизма подстановки результатов выполнения команд (9.ыу. -d 16.08 % Is -Id "find . -print" Команда./?^ выполняется, и ее вывод замещает строку между обратными кавычками. Команда *^j£ Is видит только вывод команды find и даже не подозревает о том, что она была использована. ^k/^ Альтернативный способ передачи списка файлов — использование команды xargs (Ч.И). ^^ Команды xargs к find прекрасно работают вместе. Команда xargs исполняет команды, указанные f'-#J в качестве ее аргументов, и читает стандартный ввод для определения аргументов этих команд. «Манда find: поиск фатов 255
17.03 i Она знает максимальное количество аргументов, которое допустимо в командной строке в препятствует его превышению. В то время как команда ' и % Is -Id 'find . -print' может породить ошибку, если командная строка слишком длинная, следующая равноценная ей команда работает отлично: % find . -print | xargs Is -Id - BB 17.03 He забывайте добавлять оператор -print Иногда я прихожу в недоумение: почему команда find не нашла мой файл? Ведь я точно знаю, что он есть! Очень часто мы забываем включить оператор -print. Без этого оператора (или оператора -Is если команда find его поддерживает) команда find может не отображать имен файлов. [Это' наверное, та особенность команды find, которая смущает и сбивает с толку начинающих пользователей больше, чем любая другая. — TOR] Некоторые версии команды find выводят сообщение об ошибке или же автоматически добавляют отсутствующий оператор -print, но лучше на это не рассчитывать. - JP 17.04 Поиск файлов по шаблону Аргумент оператора -name можно задать в виде шаблона, содержащего метасимволы интерпретатора shell (26.02) (а не в виде регулярного выражения, используемого в команде grep). Поскольку интерпретатор shell также видит метасимволы, их необходимо защищать (8.н) — в таком случае они будут передаваться команде find в неизменном виде. Для этой цели можно использовать кавычки любого вида или обратную косую черту: % find . -name \*.o -print % find . -name '*.o' -print % find . -name "[a-zA-Z]*.o" -print Все имена каталогов в путевом имени файла при сравнении с шаблоном не учитываются, сравнивается только имя файла. Например, приведенные выше команды не найдут совпадений для файла ./subdir.o/afde, а для файлов ./subdir.o и ./src/subdir/prog.o — найдут. Способ задания имен каталогов в шаблоне описан в параграфе 17.24. [Вот команда, задающая поиск файлов: alias ff "find . name '*\!(*)*' -Is" Укажите ей имя файла или каталога, и вы получите длинный список имен всех файлов или каталогов, имена которых совпадают с указанным. Например: % ff maJcedirs 415863 3 -rw-r—r— 1 359 daemon 2072 Feb 19 1994 . /adir/malcedirs. sh Очень удобно. — JP\ - BB 17.05 Поиск старых файлов При необходимости найти, скажем, файл семидневной давности используйте оператор -mtime. % find . -mtime 7 -print Альтернативный способ получить тот же результат — задать диапазон времени: % find . -mtime +6 -mtime -8 -print 256 Часть третья. Работа с файловой системой
17.06 Оператор -mtime определяет время последней модификации файла. Если вы хотите найти неиспользуемые файлы, проверяйте время доступа с помощью оператора -atime. Следующая команда выводит имена файлов, к которым никто не обращался уже более 30 дней: % find . -type f -atime +30 -print Найти каталоги, к которым не было доступа, сложно, поскольку команда find сама изменяет время доступа. Есть еще одна временная характеристика, связанная с каждым файлом. Речь идет о ctime — времени изменения индексного дескриптора (1.22). Доступ к этой характеристике обеспечивается оператором -ctime. Значение ctime изменяется, если изменяются имя владельца или группы пользователей, права доступа или количество ссылок, при условии, что сам файл остается неизменным. Чтобы найти файлы с определенным количеством ссылок, используйте оператор -links. Дополнительная информация об этих трех временных характеристиках приведена в параграфе 16.05. В параграфе 17.05 рассказывается, как их проверяет команда find. - ВВ 17.06 Станьте специалистом по операторам поиска команды find По общему признанию, команда find является достаточно сложной. Насколько сложной, вы сможете оценить, лишь познакомившись с ее возможностями. Для начала предлагаю рассмотреть простую команду find. % find . -name "*.c" -print Точка (.) заставляет команду find начать поиск в текущем каталоге (i.2i) и вести его во всех подкаталогах данного каталога. Оператор -name "*.c" (ПМ) сообщает, что искать нужно файлы, чьи имена заканчиваются на . с. Оператор -print указывает команде find, что нужно сделать далее, а именно: направить имена в стандартный поток вывода. Все команды find, независимо от их сложности, являются производными друг от друга. С их помощью вы можете одновременно искать файлы с различными именами, старые файлы и т.д. Но в любом случае вам нужно задать только точку начала и некоторые параметры поиска, а также определить, что делать с найденными файлами (каталогами, ссылками). Чтобы усвоить более сложные способы использования команды find, необходимо осознать, что параметры поиска на самом деле являются логическими выражениями, которые вычисляются командой find. Это значит, что команда find: • анализирует все файлы по очереди; • использует информацию, содержащуюся в индексном дескрипторе (1.22) файла, для вычисления значения выражения, заданного операторами командной строки; • предпринимает специальные действия (например, выводит имена файлов), если выражение истинно. Итак, оператор -name "*.c" на самом деле является логическим выражением, значение которого становится истинным, если имя файла заканчивается на . с. Освоив такой способ мышления, вы сможете легко использовать логические операторы AND, OR и NOT для группирования операторов. Поэтому подумаем о более сложной команде find. Давайте найдем файлы с именами, заканчивающимися на . о или . tmp, выберем из них (AND) те файлы, которым уже более пяти дней, и (AND) выведем их имена. Нам нужно использовать выражение, значение которого истинно для файлов с именами, заканчивающимися на *. о или (OR) * . tmp: -name "*.o" -о -name "*.tmp" Если любое из условий истинно, необходимо проверить время доступа. Поэтому мы берем приведенное выше выражение в скобки (поставив перед ними обратную косую черту а.н), чтобы интерпретатор shell не рассматривал скобки как операторы запуска порожденного интерпретатора (Ш)7)). К тому же нужно добавить оператор -atime (n.osy. -atime +5 \( -name "*.о" -о -name "*.tmp" \) йжяндя find: поиск файлов 9 9-171 257
17.07 Скобки заставляют команду find вычислять заключенное в них выражение как единое целое* Полученное значение будет истинным, если "время доступа превышает 5 дней и (\ () ИМя'5 заканчивается на .о или .tmp (\))"- Если не использовать скобок, то выражение будете означать нечто другое: -atime +5 -name "*.o" -о -name "*.tmp" Неправильно! '■ Команда find воспринимает два соседних оператора без оператора -о между ними как таковые над которыми должна быть выполнена операция логического И (AND). Поэтому последнее- выражение истинно, если "либо время доступа превышает 5 дней и имя заканчивается на .о, либо имя заканчивается на .tmp". Это выражение будет истинным для любого имени заканчивающегося на . tmp, независимо от того, как давно был доступ к файлу — оператор -atime не действует. (Подобное выражение вполне корректно, тем не менее нашим требованиям оно не соответствует. Команда find примет это выражение и сделает то, что от нее требуется, но только не то, что нам нужно.) Следующая команда перечисляет файлы текущего каталога и его подкаталогов, удовлетворяющие нашим критериям: % find . -atime +5 \( -name "*.о" -о -name "*.tmp" \) -print А что если мы захотим перечислить все файлы, не удовлетворяющие этим критериям? Для этого нам нужно всего лишь логически инвертировать данное выражение. Оператор НЕ (NOT) записывается как восклицательный знак (!) и относится к выражению, стоящему справа от него. Поскольку мы хотим применить его ко всему выражению, а не только к оператору -atime, то нужно заключить всю строку от atime до "*. tmp" в еще одни скобки: % find . ! \( -atime +5 \( -name "*.о" -о -name "*.tmp" \) \) -print Согласно изложенной концепции, оператор -print также является выражением, которое всегда истинно. Сказанное относится и к операторам exec и ок (п.щ. Их значения истинны, если выполняемые ими команды возвращают нулевой код. (В некоторых случаях этим обстоятельством можно воспользоваться — см. параграф 17.11.) О выражениях команды find мы поговорим также в параграфе 17.12. Но прежде чем пробовать сделать что-нибудь действительно сложное, следует уяснить одну вещь: команда find не настолько умна, насколько хотелось бы. В выражении нельзя сжать все пробелы, как это обычно делается в настоящих языках программирования. До и после таких операторов, как !, \ (, \) и {}, необходимо вводить пробелы, равно как и после любого другого оператора. Следовательно, строки, подобные представленной ниже, работать не будут. % find . !\(-atime +5 \(-nama "*.o" -о -name "*.tmp"\)\) -print Действительно, грамотный пользователь должен понимать, что команда find при разбиении командной строки на значимые элементы (S.nS), или лексемы, полагается на интерпретатор shell. A интерпретатор, в свою очередь, предполагает, что лексемы разделены пробелами. Когда интерпретатор shell предоставляет команде find такую цепочку символов, как *. tmp)) (без двойных кавычек и обратной косой черты — он их убирает), то команда find может "запутаться", так как считает, что имеется в виду странный шаблон имени файла с двумя скобками. Когда вы начнете мыслить категориями выражений, синтаксис команды find перестанет быть загадочным. В каком-то смысле, его даже можно назвать элегантным. Он позволяет четко и эффективно выразить ваши мысли и описать требуемые действия. - ML, JP 17.07 Задание параметра времени в команде find Особенности задания значений времени в таких операторах команды find, как -mtime, -atime и -ctime (пму, документированы недостаточно полно. • Числу без знака, например 3 (как в выражениях -time 3 или -atime 3), соответствует 24-часовой период, закончившийся точно три дня назад (иными словами, 72—96 часов назад). 258 Часть третья. Работа с файловой системой-
17.09 • Число, содержащее минус (-), относится к периоду, истекшему с того времени (три дня назад). Например, -3 (как в выражении -mtime -3) соответствует периоду между настоящим временем и тремя предыдущими днями (иначе говоря, 0—72 часа назад). • Число, содержащее плюс (+), естественно, относится к периоду, истекшему до того времени (три дня назад). Например, +3 (как в выражении -mtime +3) соответствует любому времени, предшествующему трем последним дням (иными словами, более 96 часов назад). Несложно заметить, что время всегда задается в днях. Усвоили? Тогда вам должно быть ясно, что оба выражения, и -atime -2, и -atime 1, истинны для файлов, к которым был доступ в период от 48 до 24 часов назад. (Выражение -atime -2 также истинно для файлов, доступ к которым был не более чем 24 часа назад.) Для проведения более точных сравнений используйте команду find -newer с командой tOUCh (17.08). - JP 17.08 Поиск файлов с точно заданными временными характеристиками Одна из проблем, с которыми приходится сталкиваться при использовании оператора -atime и родственных ему операторов (i7.os.t7.07), — это невозможность проведения точных сравнений. Вы можете определить время с точностью только до суток. Но такая точность не всегда устраивает. К примеру, вы предполагаете, что работа вашей системы была нарушена приблизительно в 4 часа пополудни вчера, 20 марта, и хотите выяснить, какие файлы были изменены после того (их следует проверить). Очевидно, в данном случае необходимо применить какую-то более точную команду, чем "вывести имена всех файлов, модифицированных за последние 24 часа". Некоторые версии команды touch gi.oT) и подобные ей другие свободно распространяемые команды могут создавать файлы с произвольными временными метками. Это значит, что с помощью команды touch можно создать файл, помеченный любым временем в прошлом (или, аналогичным образом, — в будущем). Эта возможность в сочетании с оператором -newer команды find позволяет проводить сравнение времени с точностью до 1 минуты и выше. Чтобы создать файл, датированный, скажем, 4 часами пополудни 20 марта, выполните команду /пор 21.02 % touch 03201600 /tmp/4EMyesterday Затем, чтобы найти все файлы, созданные после указанного времени, выполните команду % find . -newer /tmp/4PMyesterday -print А как быть, если необходимо выбрать более старые файлы? Команда find имеет удобный оператор НЕ (!), предназначенный как раз для этой цели. Предположим, вам нужно найти файлы, созданные в период между 10 часами 46 минутами 3 июля 1982 года и 21 часом 37 минутами 4 августа 1985 года. Для этого можно выполнить такие команды: 1Ш9] ^(/ % touch 0703104682 /tmp/filel '-^ % touch 0B04213785 /tmp/file2 % find . -newer /tmp/filel ! -newer /tmp/file2 -print % rm /tmp/file[12] - ML 17.09 Проблемы с оператором -newer Если попытаться использовать оператор -newer дважды в одной команде, то можно столкнуться с определенной проблемой. Пусть, скажем, мы хотим найти все файлы, более новые, чем filea, но более старые, чем flleb. Очевидный способ решить задачу — выполнить следующую команду: % find . -newer filea ! -newer filab -print Команда find; поиск файпов 9» 259
Однако большинство версий команды find могут одновременно работать только с одной да Когда команда find прочитает дату файла fileb, она сбросит дату файла filea, и в обоих операт. будет использована дата файла fileb. Поэтому на самом деле команда будет искать более новш файлы, чем fileb, но более старые, чем fileb, и, конечно же, вообще ничего не найдет. Из такого положения можно выйти, определив количество дней, прошедших с тех пор, к^. файлы filea и fileb были модифицированы, и переписав командную строку с использованием двух операторов -mtime. Как и в случае любых ошибок (или "особенностей"), некоторые поставщики могли уд» обнаружить и устранить описанную проблему — в таком случае вам не придется с ней сталкиваться в своей системе. Выражения с двумя операторами -newer команды find хороцщ работают в SunOS 4.1, но не работают в предыдущих реализациях SunOS. По-видимому, они функционируют и в SVR4. Имеющаяся на компакт-диске GNU-версия команды find не содержит данной ошибки. - ML 17.10 Выполнение команд в случае успешного поиска [При поиске файла вы часто не просто хотите увидеть его имя, но и выполнить какое-то действие, например найти строку текста с помощью команды grep. Чтобы сделать это используйте оператор -exec. Он позволяет указать команду, которая должна выполняться для каждого найденного файла. — TOR] Синтаксис оператора -exec довольно специфичен. Во многих случаях проще направить вывод команды find по каналу команде xargs (17.02). Тем не менее, бывают случаи, когда нуясно использовать именно оператор -exec, поэтому давайте обсудим его особенности. Оператор -exec позволяет выполнить любую команду, в том числе другую команду find. Если вы задумаетесь над сказанным, то поймете, что команда find должна как-то отличать выполняемую ею команду от собственных аргументов. Очевидное решение — использование того же символа конца команды, что и в интерпретаторе shell (т.е. точки с запятой). Поскольку интерпретатор shell сам использует точку с запятой (s.os), то необходимо изменить назначение этого символа при помощи обратной косой черты или кавычек. Следовательно, каждый оператор -exec заканчивается символами \;. Есть еще один специальный аргумент, {}, который команда find рассматривает особым образом. Эти два символа используются как переменная, значением которой является имя файла, найденного командой find. He старайтесь перечитывать последнее предложение. Пример прояснит использование этого аргумента. Рассмотрим тривиальный случай, когда оператор -exec используется вместе с командой echo (s.06), имитирующей действия оператора -print. % find . -exec echo {} \; Интерпретатор С shell также использует символы { и } (9.os), но не изменяет символов {}, если они стоят вместе, вот почему перед ними обратная косая не нужна. Однако точка с запятой должна быть защищена. Вместо обратной косой черты можно использовать кавычки: % find . -exec echo {} ';' Оба этих символа скрывают точку с запятой от интерпретатора shell и передают ее команде find. Как я уже говорил, команда find может даже вызвать другую команду find. Если нужно перечислить все символические ссылки во всех каталогах, принадлежащих группе staft, рекомендуем выполнить команду '...' 9.16 % find 'pwd~ -type d -group staff -exec find {} -type 1 "print \; Чтобы найти все файлы с правом записи для группы пользователей и удалить соответствующий атрибут, можно воспользоваться командой -рот 17.15 % find . -perm -2Q -exec chmod g-vf {} \; ИЛИ % find . -perm -20 -print | xargs chmod g-w 2S0 Часть третья. Работа с файловой систШ*
17.11 Между работой оператора -exec и команды xargs разница незначительная. Оператор -exec выполняет программу один раз для каждого файла, в то время как команда xargs может обрабатывать несколько файлов в одном процессе. Нужно помнить, что указанная команда иногда вызывает проблемы (9.22) при использовании имен файлов с пробелами. Случается, что пользователи создают "странные" файлы, не поддающиеся удалению. Это может произойти, если имя файла будет содержать пробел или управляющий символ. Команда find и оператор -exec помогут удалить такой файл, в то время как команда xargs — нет. Попробуйте воспользоваться командой Is -il, что позволит вывести список файлов с номерами индексных дескрипторов о.22). Удалить файл можно с помощью операторов -inum и -exec (23.щ: % find . -inum 31246 -exec rm {} ' ; ' При желании вы можете воспользоваться оператором -ок, который делает то же самое, что оператор -exec. Правда, программа в этом случае, прежде чем выполнять действие, запросит подтверждение. И это будет не лишним, так как при выполнении команды find могут появиться опасные ошибки. Если у вас возникают какие-либо сомнения, используйте команду echo. Можно также направить вывод в файл и исследовать его перед использованием в качестве ввода для команды xargs. Вот как я обнаружил, что команда find требует применения отдельно стоящих фигурных скобок ({}) в списке аргументов оператора -exec. Мне нужно было переименовать некоторые файлы с помощью оператора -exec mv {} {}.orig, но команда find не заменила скобки в выражении U.orig. Я выяснил, что нужно написать сценарий (i7.il), который должна выполнять команда find. [Небольшой цикл while (44.10) для интерпретатора Bourne shell с переадресованным вводом (45.23) также может справиться с этой задачей: $ find ... -print | > 17.15 > while read file > do mv "$file" "$file.orig" > done Команда/?/^ направляет имена файлов на стандартный вывод. Цикл while и команда read (44.13) читают имена файлов со стандартного ввода, а затем по очереди записывают их в переменную $file. - JP] Примеры использования оператора -exec приведены также в параграфах 17.12 и 17.24. - ВВ 17.11 Использование оператора -exec для создания пользовательских тестов Рассмотрим ситуацию, столкнувшись с которой, можно легко запутаться. Как вы помните, оператор -exec имеет значение true лишь при условии, что выполняемая им команда возвращает нулевой код (.44.07). Этим обстоятельством можно воспользоваться при построении пользовательских тестов с командой find. Предположим, вы хотите получить список "прекрасных" файлов. Вы написали программу beauty, которая возвращает нуль, если файл по какому-то признаку "прекрасен", или какое-либо другое значение — в противном случае. (Эта программа может быть сценарием интерпретатора shell (44.ii), рет-Рсценарием pzoi), исполняемым модулем программы на С и т.д.) Например: % find '. -exec beauty {} \; -print В этой строке -exec является просто одним из операторов команды find. В отличие от других операторов нас интересует его значение — мы не предполагаем, что оно всегда истинно. Команда find выполняет команду beauty для каждого файла. Затем оператор -exec принимает значение true, если найден "прекрасный" файл, и команда find выводит имя этого файла. (Прошу прощения: команда find сначала определяет значение оператора -print. :-)) Команда find: поиск файлов 261
17.12 Конечно, этот прием можно использовать в самых разных вариациях. Если вам необходим)! найти "прекрасный" файл, содержащий С-код, можете воспользоваться такой командой: % find . -name "*.[ch]" -exec beauty {} \; -print И так далее. Чтобы добиться более высокой производительности, оператор -exec лучцю] помещать поближе к концу строки. Это поможет избежать запуска ненужных процессов;! оператор -exec будет выполняться только при условии, что значения предыдущих операторов истинны. л - JP, ML ■, 17.12 Поиск несвязанных объектов с помощью одной команды На выполнение команды find тратится много времени, и тому имеется объяснение; нужно прочесть каждый индексный дескриптор (1.22) в дереве каталогов, где производится поиск." Поэтому есть смысл объединить в одной команде поиск как можно большего количества объектов. Если уж вы собираетесь "прогуляться" по всему дереву, выполните заодно как можно больше полезной работы. Будем отталкиваться от примера. Предположим, вам нужно написать команду, которая" позволяет правильно установить права доступа (22.02) к файлам. Для каталогов вы решили'; установить восьмеричное значение права доступа равным 771, для всех резервных файлов"; (* .ВАК) — равным 600, а для всех текстовых файлов (*. txt) — равным 644. Все это можно сделать посредством одной команды: $ find . \( -type d -a -exec chmod 771 {} \; \) -о \ \( -name "*.BAK" -a -exec chmod 600 {} \; \) -о \ \( -name "*.sh" -a -exec chmod 755 {} \; \) -о \ Ч \{ -name "*.txt" -a -exec chmod 644 {} \; \) ' Почему это работает? Как вы помните, оператор -exec на самом деле является всего лишь частью выражения. Его значение истинно, когда следующая за ним команда выполняется* успешно. Этот оператор не производит независимых действий, которые каким-то образом влияли бы на всю работу команды find. Следовательно, оператор -exec можно свободно: комбинировать с операторами -type, -name и другими (см. параграф 17.11). Однако здесь применен еще один трюк. Взгляните на первую строку команды. Она означает' следующее: "Если этот файл является каталогом и команда chmod выполняется успешно..." Стоп! Почему оператор -exec не выполняет команду chmod для каждого файла в каталоге,' проверяя успешность ее завершения? Логические выражения вычисляются в направлении слева направо, и в каждом члене выражения вычисления прекращаются, когда становится ясным результат. Рассмотрим логическое выражение '"A AND В' = true". Если А равно false, то значение выражения "'A AND в' = true",. понятно, также будет равным false, поэтому нет смысла обращать внимание на В. Так, в приведенном выше многострочном выражении команда find при поиске файла проверяет, является ли данный файл каталогом. Если да, то оператор -type d принимает значение true и команда find выполняет оператор -exec (изменяя права доступа к файлу)У:; Если файл не является каталогом, то команда find знает, что все выражение будет ложным, поэтому на выполнение оператора -exec не стоит тратить время. Команда переходит не следующую строку. И, конечно же, нет необходимости всем операторам -exec обязательно выполнять одну и ту же команду. Одни из них могут удалять файлы, другие — изменять режим доступа, третьи — перемещать файл в другой каталог и т.д. И последнее замечание. Хотя понять нашу многострочную команду find нелегко, на самом деле она ничем не отличается от обычной команды. Подумайте, что делает следующая команда: % find . -name "*.c" -print 262 Часть третья. Работа с файловой систеМО*
17.14 Здесь есть два оператора: -пате, значение которого истинно, когда имя файла заканчивается на .с, и -print, значение которого всегда равно true. Два оператора объединены посредством логического И (мы могли бы поместить между ними оператор -а, и это не привело бы к изменению результата). Если значение оператора -пате оказывается ложным (т.е. имя файла не закачивается на . с), то команда find знает, что все выражение будет ложным. Поэтому она не обращает внимания на оператор -print. Но если значение оператора -пате равно true, то команда find выполняет оператор -print, что приводит к выводу имени файла. Как было отмечено в параграфе 17.06, работа команды find состоит в вычислении выражений, а не в поиске файлов. Да, эта команда ищет файлы, но это, можно сказать, побочный эффект. Осознание этого обстоятельства помогло мне использовать команду find намного эффективнее. - ML 17.13 Поиск файлов по их типу Если вас интересуют только файлы определенного типа, используйте оператор -type с указанием типа файла (табл. 17.1). [Отдельные версии команды find работают не со всеми из указанных типов файлов. — JP\ Табпицз 17.1. Символы, определяющие типы файлов, с которыми работает команда find -type Символ ь с d f 1 р S Тип файпа Специальный файл блок-ориентированного устройства Специальный файл байт-ориентированного устройства Каталог Обычный файл Символическая ссылка Файл именованного канала Гнездо Если вы не являетесь системным администратором, то наибольший интерес для вас представляют каталоги, файлы и символические ссылки (т.е. типы d, f и 1). Оператор -type предоставляет еще один способ рекурсивного вывода списка файлов: xargs 931 % find . type f -print | xargs Is -1 Отследить все символические ссылки каталога не всегда просто. Предлагаемая далее команда находит все символические ссылки в вашем начальном каталоге и выводит имена файлов, на которые эти ссылки указывают. [Значение $NF определяет последнее поле в каждой строке, содержащее имя, на которое указывает символическая ссылка. — JP] Если ваша команда find не имеет оператора -Is, передайте данные по каналу команде xargs, как в предыдущем примере: % find $HOME -type 1 -Is | awk '{print $NF}' - BB 17.14 Поиск файлов по размеру Команда find имеет несколько операторов, принимающих в качестве аргумента десятичное число. Одним из таких операторов является -size. Число после этого оператора указывает размер файла в дисковых блоках. К сожалению, это не точный параметр. Ранние версии UNIX использовали дисковые блоки размером 512 байтов. В более новых ее версиях применяются блоки больших размеров, поэтому ориентация на блок в 512 байтов может привести к неправильному результату. "Команда find: поиск файлов 263
17.15 J 4 Избежать такого рода проблем вам поможет команда Is -s. Опция -s, как правило, так**'» выводит размеры файлов в блоках. Но если ваша система имеет другой размер блока, чекг предполагали разработчики команды Is -s, то и она может дать неправильный результат Введите после числа, определяющего размер файла, символ с, и размер будет задаваться в байтах. Чтобы найти файл размером, скажем, 1234 байта, введите следующее: % find . -size 1234c -print При поиске файлов, размеры которых лежат в определенном диапазоне, перед числом можно поставить знак минуса или знак плюса. Первый означает "меньше чем", а второй — "больше чем". Ниже выводятся имена файлов размером более 10000 байтов, но менее 32000 байтов: % find . -size +10000c -size -32000c -print Если заданы два оператора, они оба должны быть истинными (П.ов). - В В 17.15 Поиск файлов с заданными правами доступа [Если вы при использовании в UNIX восьмеричных чисел, в частности для задания прав доступа чувствуете себя неуверенно, рекомендуем очень внимательно прочитать параграф 1.23. — JP] Команда find может искать файлы с заданными правами доступа. Она использует восьмеричное число для кодирования этих прав. Строка rw-rw-r— означает, что вы и члены вашей группы имеете право на чтение и запись, в то время как все остальные — только право на чтение. Те же права доступа выражаются восьмеричным числом 664. Чтобы найти все файлы типа *.о и с указанными правами доступа, используйте команду % find . -name \*.o -perm 664 -print Для того чтобы узнать, есть ли каталоги с правом на запись для всех, используйте команду % find . -type d -perm 777 -print В приведенных выше примерах мы ищем только точные соответствия указанному значению прав доступа. Если вы хотите найти все каталоги с правом на запись для группы пользователей, нужно найти соответствие шаблону w . Существует несколько комбинаций, которые могут подойти. Вы можете перечислить все комбинации, но команда find позволяет задать шаблон, который будет побитно просуммирован (AND) с кодом прав доступа файла, Просто' поставьте перед восьмеричным значением шаблона знак минуса. Бит права на запись для группы пользователей соответствует восьмеричному числу 20, поэтому команда % find . -perm -20 -print найдет файлы с правами доступа, перечисленными ниже. Права rwxrwxrwx rwxrwxr-x rw-rw-rw- rw-rw-r— rw-rw Восьмеричное значение 777 775 666 664 660 Чтобы найти файлы, которые можно выполнять (например, сценарии или программы), нужно задать шаблон --х с помощью команды % find . -perm -100 -print Когда в значении аргумента оператора -perm присутствует минус, то сравниваются все биты, в том числе и биты SUID (1.23). - ВВ 264 Часть третья. Работа с файловой системы
17.18 17.16 Поиск файлов по имени владельца или группы пользователей Иногда нам необходимо найти файлы, принадлежащие определенному пользователю или группе пользователей. Это делается с помощью операторов поиска -user и -group. Бывает, что такой поиск нужно связать с определенными правами доступа. Чтобы найти все файлы пользователя root с установленным битом смены идентификатора пользователя (SUID) о.23>, примените команду % find . -user root -perm -4000 -print При необходимости найти все файлы, скажем, группы staff с установленным битом смены идентификатора группы (SGID) р.23) воспользуйтесь командой % find . -group staff -perm -2000 -print Знать имя пользователя или группы пользователей из файла /etc/passwd (.н.оз> или /etc/group (22.13) не обязательно — вы можете задействовать соответствующие идентификаторы (идентификатор пользователя или группы пользователей (за.оз)): % find . -user 0 -perm -4000 -print % find . -group 10 -perm -2000 -print Довольно часто, когда пользователь покидает узел, его учетная запись удаляется, но его файлы продолжают оставаться на компьютере. Некоторые версии команды find имеют операторы -nouser и nogroup, предназначенные для поиска файлов с неизвестными идентификаторами пользователя или группы пользователей. - ВВ 17.17 Цублирование дерева каталогов Оператор { } команды find, используемый одновременно с оператором -exec (П.щ, работает только в том случае, если он отделен от других аргументов символом пробела. Поэтому приведенная ниже команда не будет делать того, что вы от нее ожидаете: % find . -type d -exec mkdir /usr/project/{} \; Вы, по-видимому, хотели, чтобы эта команда создала в каталоге /usr/project набор пустых каталогов, рекурсивно дублирующих подкаталоги текущего каталога. Если команда find находит, например, каталог ,/adir, то должна выполняться команда mkdir /usr/pro- ject/./adir (точка игнорируется, и в результате создается каталог /usr/project/adir (Up). Но команда find не распознает оператора {} в путевом имени. Поэтому имена каталогов ; необходимо передать программе sed_ (34.24), которая выполняет подстановку в путевом имени: % find . -type d -print | sed 's@A@/usr/project/@' | xargs mkdir % find . -type d -print | sed 's@A@mkdir @' | (cd /usr/project; sh) Начнем с первого примера. Получив список каталогов, редактор sed подставляет перед именем каталога требуемый путь и передает полные путевые имена командам xargs (9.21) и mkdir. Символ @ в редакторе sed выполняет роль разделителя (З4.»т), поскольку косая черта используется в самом подставляемом тексте. Если у вас нет команды xargs, попробуйте реализовать второй пример. В нем редактор sed использован для вставки команды mkdir, a далее, в порожденном процессе (М.07), выполняется переход в каталог, в котором и будет произведена команда mkdir. - JP 17.18 Использование "быстрой" команды find Команда find версии Berkeley обладает очень удобной возможностью: если ей передается единственный аргумент, то она ищет подходящий файл или каталог в специально созданной для этого базе данных. (Если ваша система не поддерживает эту возможность, обратитесь к описанному ниже сценарию locate.) Зная, например, что где-то в компьютере есть файл с именем MH.eps, вы сможете его найти, если введете следующее: % find MH.eps /nutshell/graphics/cover/MH.eps Цюеда find: поиск файпов 265
17.18 4 \ Обычно база данных перестраивается каждую ночь, поэтому мы можем считать ее достаточна' полной. Если ваш системный администратор создал такую базу, то в ней, как правилок перечисляются все файлы файловой системы, хотя файлы, находящиеся в каталогах, ц«? которые права общего доступа не распространяются, в ней могут отсутствовать. Если база* данных вообще не установлена, вы получите сообщение об ошибке типа /usr/iib/fincj/ find, codes: No such file or directory. В этом случае вы можете самостоятельно установить базу данных для "быстрой" команды find, воспользовавшись описанным ниже GNU-сценарием locate (см. также параграф 17.19). Если вы не задействуете метасимволов, быстрая команда find, как и команда &гец рщ осуществляет обычный поиск строки в списке полных путевых имен (им). Простейший пример- % find bin /bin /bin/ar /home/robin /home/robin/afile /home/sally/bin Вы можете урезать этот вывод, пропустив его через фильтры grvp (27.oi), sed (34.24) и другие. Все использованные мною "быстрые" версии команды find имели недокументированную возможность: они понимали метасимволы (*, ?, []) (is.o2). Обрабатываются метасимволы точно так же, как в интерпретаторе shell. Различие в способе интерпретации метасимволов в shell и в быстрой команде find состоит, в частности, в том, что интерпретатор обрабатывает знак косой черты особым образом — он всегда должен являться частью выражения. Быстрая команда find сравнивает этот символ, как и любой другой. Не забудьте заключить используемые метасимволы в кавычки, чтобы интерпретатор shell их не трогал. Приведем несколько примеров. • Поиск путевого имени, заканчивающегося на bin: % find '*bin' /bin /home/robin /home/robin/bin • Поиск путевого имени, заканчивающегося на /bin (хороший способ поиска файла или каталога с точным именем Ып): % find "*/Ы.п' /bin /home/robin/bin /usr/bin • Набрать find '*bin*' — это то же, что и набрать find bin. • Чтобы получить список файлов в каталоге Ып, а не имя самого каталога, попробуйте ввести такую команду: % find '*/bin/*' /bin/ar /bin/cat /home/robin/bin/prog • Поиск в каталоге /home файлов, имена которых заканчиваются знаком тильды (~) (это могут быть резервные копии файлов редактора Emacs), осуществляется так: % find '/home/*-' /home/testfile- 266 Часть третья. Работа с файловой системой
17.19 /home/allan/.cshrc- /home/allan/.login~ /home/dave/.profile- Обратите внимание: звездочка в быстрой команде find также соответствует именам файлов с точкой. • Специальные символы ? (вопросительный знак) и [] (квадратные скобки) в быстрой команде find также работают. Правда, здесь они не настолько полезны, как в интерпретаторе shell, поскольку соответствуют и символам косой черты (/) в путевых именах. Вот несколько примеров: % find •????' /bin /etc /lib /src /usr % find '/[bel]??' /bin /etc /lib К сожалению, быстрая команда find есть не во всех системах. Но благодаря организации Free Software Foundation мы можем воспользоваться сценарием locate. Он подобен быстрой команде find, однако имеет одно преимущество: позволяет иметь несколько баз данных для файлов и осуществлять поиск в некоторых или во всех из них. Сценарий locate поставляется вместе с программой построения базы данных. Быстрая команда find и сценарий locate, характеризующиеся столь высокой скоростью работы, заслуживают того, чтобы их применяли постоянно. Передаете ли вы их вывод по каналу команде xargs (9.21) или любой другой команде UNIX, сценарию shell или аи»А>сценарию для проверки выходных данных — почти все будет сделано быстрее, чем при использовании обычной команды find. Если вам, предположим, необходимо получить развернутый список файлов, воспользуйтесь командами find, приведенными ниже: :ijS.os % Is -Id 'find шаблон' % find шаблон | xarga Is -Id Однако не исключено, что вы при этом столкнетесь с проблемой. База данных быстрой команды find может быть создана пользователем root, который видит все файлы системы. Ваша же команда Is -l может не получить доступ ко всем файлам подобного списка. - JP 17.19 Ускоренный поиск файлов с помощью базы данных команды find Вы уже знаете, что при использовании команды find (п.02) на поиск файлов может уйти много времени, особенно если он осуществляется в больших каталогах. Приведем несколько идей, реализация которых позволит вам избежать потерь. Примечание: Изначально методы, предназначенные для построения базы данных, не позволяют получить актуальной информации о всех файлах. При наличии в системе быстрой команды find или GNU-сценария locate (i7.1t) вы, возможно, больше ни в чем не нуждаетесь. Ведите поиск в списке путевых имен системы. Правда, упомянутые выше команды не всегда могут вас удовлетворить. Например, они могут искать только путевые имена. Чтобы найти файл по имени владельца, количеству ссылок, Команда find: поиск файлов 267 В locale
17.19 размеру и т.д., вам приходится использовать "медленную" команду find. В таких случаях, а также если у вас нет быстрой команды find win сценария locate, вам нужно создать собственную версию быстрой команды find. Для работы быстрой команды find необходимо иметь две утилиты. Одна из них — сценарий с' именем /usr/lib/fmd/updatedb, который оптимальным образом строит базу данных имен файлов вашей системы. Другая — сама команда find. Она производит в базе данных поиск путевых имен, которые соответствуют введенному вами имени (регулярному выражению). Чтобы создать собственную быструю команду find, необходимо выполнить действия перечисленные далее. 1. Выберите для базы данных имя. Мы остановились на имени $HOME/.fastfind (в некоторых системах вместо $НОМЕ используется $LOGDfR). 2. Сконструируйте команду find для создания базы данных, отвечающей вашим требованиям. Команда построения базы данных для всех файлов вашего начального каталога должна иметь примерно такой вид: cd find . -print I sed "s@A./@@" > $HOME/.fastfind При ограниченном дисковом пространстве рекомендуется использовать команду cd gzip24.07 find ■ -print | sed "s@A./@@" I gzip > $HOME/.fastfind.gz С целью экономии дискового пространства редактор sed (34.24) удаляет начало путевого имени (такого как . /) из каждой записи. (Если строится база данных для всей системы, этого делать нельзя!) 3. Сконфигурируйте команду сгоп (40.12) или at (40.03) на запуск команды find, который будет выполняться настолько часто, насколько вы посчитаете нужным. Обычно это делается один раз в день. 4. Для поиска в базе данных создайте сценарий (i.os) (свой я назвал ffind). Быстрее всего обычно работает команда egrep (27.05), которая позволяет вести поиск с помощью гибких регулярных выражений (2бму. egrep "$1" $HOME/.fastfind I sed "s@A@SHOME/@" Для базы данных, сжатой с помошью команды gzip, нужен другой сценарий: gzcat 24.07 gzcat $НОМЕ/.fastfind.gz I egrep "$1" I sed "s@A@$HOME/@" Фильтр sed добавляет к каждой строке путевое имя вашего начального каталога (например, /usr/freddie). Чтобы провести поиск в базе данных, наберите % ffind someflle /usr/freddie/lib/somefile % ffind '/(seploct)[*/]*$' /usr/freddie/misc/project/September /usr/freddie/misc/project/October С помощью этого сценария можно делать и многое другое. Я лишь подам вам идею. При наличии места для хранения не только простых путевых имен, но и другой информации, вывод своей команды find можно направить таким командам, как Is -I или sis (I6.29). Например, если вы много работаете со ссылками (is.03), то наряду с именами файлов вам, возможно, понадобится хранить и номера индексных дескрипторов (1.22). В таком случае базу данных нужно было бы построить с помощью команды, аналогичной приведенной ниже. Кроме команды xargs (9.21), можно воспользоваться подобной ей командой (9.2И). cd find . -print I xargs Is -id > $HOME/.fastfind 268 Часть третья. Работа с файловой системой
17.20 к Если ваша версия команды find уже имеет удобный оператор -Is, используйте следующий сценарий. ; Проследите, не появляются ли у вас очень большие значения номеров индексных дескрипторов. |: Это может привести к сдвигу столбцов и некорректному выводу команды cut (js.u). cd find . -Is I cut -cl-7,67- > $HOME/.fastfind Количество столбцов в выводе команды Is зависит от самой системы. Далее ваш сценарий ffind сможет искать файлы по номерам индексных дескрипторов. Если, например, у вас есть файл с идентификатором 1234 и нужно найти все его ссылки, наберите % ffind "A1234 " (Пробел в конце выражения предотвратит совпадение с такими идентификаторами, как 12345, 12346 и т.п.) Поиск можно вести и по путевому имени. В параграфе 16.21 описан еще один способ организации базы данных для команды find — в виде списка каталогов или файлов с одинаковыми именами. Вооружившись описанной в данном параграфе методикой, а также сведениями по программированию в shell и утилите awk (зз./i), можно построить сложную базу данных, поиск в которой позволит получать информацию гораздо быстрее, чем с помощью обычной команды find. - JP 17.20 Поиск файлов по их содержимому (и возникающие при этом ошибки) Хотите просмотреть каждый файл в некотором каталоге и всех его подкаталогах, с тем чтобы найти файл, содержащий определенное слово или строку? Подобная работа вполне под силу команде find и одной из команд grep (27.ni). При необходимости найти, скажем, все файлы, в которых хранятся строки, начинающиеся с числа и содержащие слова SALE PRICE, можно было бы воспользоваться такой командой: % egrep 'А[0-9].*SALE PRICE' "find . -type f -print' ./archive/ad.1290: 1.99 a special SALE PRICE ./archive/ad.0191: 2.49 a special SALE PRICE Однако использование обратных кавычек р.щ не всегда приемлемо. Если команда find найдет слишком много файлов, то строка аргументов команды egrep станет слишком длинной (ч.гщ. Проблему можно решить путем применения команды xargs (9.21), которая разбивает длинные цепочки аргументов на меньшие звенья. Но и в данном случае может возникнуть проблема: если последнее звено содержит только одно имя файла и команда grep обнаружит в файле совпадение, то она не выведет имени этого файла: fgrep 27.06 % find . -type f -print I xargs fgrep ' $12. 99' ./old_sales/ad.0489: Get it for only $12.99! ./old_sales/ad.0589: Last chance at $12.99, this month! Get it for only $12.99 today. Выход состоит в том, чтобы добавить пустой файл — /dev/null оз.ну.Ъ этом файле совпадение точно никогда не будет обнаружено, а команде fgrep всегда предоставляется не менее двух имен файла: % find . -type f -print I xargs fgrep '$12.99' /dev/null Теперь команда xargs будет запускать такие команды: fgrep '$12.99' /dev/null ./afile ./bfile ... fgrep '$12.99' /dev/null ./archive/ad.0190 ./archive/ad.0290 ... fgrep '$12.99' /dev/null ./old_sales/ad.1289 Данный прием хорош и при использовании шаблона, которому соответствует только один файл. Команда grep не всегда будет выводить имя файла, если не добавить аргумент /dev/null: % grep "шаблон" /dev/null /x/y/z/a* - JP Команда find: поиск фатов 269
17.21 17.21 Сценарий lookfon поиск файлов, содержащих заданное слово Простой сценарий lookfor использует команду find azoi) для поиска в заданной иерарх! каталогов всех файлов, модифицированных в течение определенного времени, и перед найденные имена файлов команде grep (27.02) для поиска указанного шаблона. Так, коман % lookfor /work -7 tamale enchilada будет вести поиск во всей файловой системе /work и выводить имена всех файлов* модифицированных в течение последней недели и содержащих слово tamale или enchilada! (Например, если данный параграф будет сохранен в каталоге /work, то сценарий lookfor найдет* и его.) •>; Аргументами для сценария являются: путевое имя иерархии каталогов, в которой осуществц'* ляется поиск ($1), время модификации ($2), а также один или несколько текстовых шаблонов Этот простой (хотя и несколько медленный) сценарий может искать практически бесконечное* количество слов: ; <т #!/bin/sh temp=/tmp/lookfor$$ trap 'rm -f $temp; exit' 0 1 2 15 lookfor " find $1 -mtime $2 -print > $temp shift; shift for word do grep -i "$word" 'cat $temp' /dev/null done Команда grep запускается для поиска каждого слова. Опция -/ позволяет искать и заглавные, и прописные буквы. Использование аргумента /dev/null гарантирует вывод командой grep имени файла (U.I4). Имейте в виду: список имен файлов может быть слишком длинным (9.20). Следующий сценарий имеет более ограниченные возможности, но работает быстрее. Он строит регулярное выражение для команды egrep (2zos), которая находит все слова за один цикл просмотра файлов. Если будет введено слишком много слов, то команда egrep выведет сообщение Regular expression too long (слишком длинное регулярное выражение). Ваша команда egrep может не иметь опции -/, в таком случае ее можно просто опустить. В данной версии использована также команда xargs (9.21), из-за которой могут возникнуть Проблемы (9.22). (9) elookfor #!/bin/sh where="$l" when="$2" shift; shift # Построение выражения для команды egrep # типа (wordlIword2|...) и сохранение его в $ехрг for word do case "$expr" in "") expr="($word" ;; *) expr-="$exprI$word" ;; esac done expr="$expr)" find $where -mtime $when -print I xargs egrep -i "$expr" /dev/null JP, TOR 270 Часть третья. Работа с файловой системой
17.23 17.22 Поиск ссылок на файл Вот как можно найти ссылки, а заодно произвести беглый обзор файловой системы UNIX. Предположим, у нас есть такая информация: % is -li /usr/foo 2076 -rw-r—г— 3 chris 326 Sep 16 03:23 /usr/foo Другими словами, мы имеем три ссылки, a /usr/foo является одним из трех имен, соответствующих индексному дескриптору (1.22) с номером 2076. Полные имена двух других ссылок можно найти с помощью команды /etc/ncheck и (или) find. Однако знать номер индексного дескриптора не достаточно. Существует еще один скрытый номер — номер устройства. Он указывает, какая файловая система содержит нужный файл. Может быть какое угодно количество индексных дескрипторов с номером 2076, но при условии, что они содержатся в разных файловых системах. (Последние версии UNIX вместо номера устройства используют идентификатор файловой системы, предназначенный для представления последней на других компьютерах. В последних версиях UNIX также могут использоваться номера виртуальных индексных дескрипторов (vnode), а не обычных {mode). Конечный результат будет тем же, хотя запустить команду /etc/ncheck не на локальном диске часто бывает невозможно.) При необходимости выяснить, какой файловой системе принадлежит файл /usr/foo, запустите команду d[ (24.09) или mount. Предположим, имя файловой системы — /dev/sdOg. Если информация о данной системе имеет вид % df Filesystem kbytes used avail capacity Mounted on /dev/sdOg 179423 152202 9278 94% /usr % Is -1 /dev/sdOg brw 1 root 2, 6 Dec 27 07:17 /dev/sdOg то старший номер устройства равен 2, а младший — 6. Эти номера прмсзаиваются устройству с помощью макроса makedev в одном из исходных файлов ядра. Обычно информация о файле устройства записывается в виде старший*256 + младший. В данном случае мы имеем 2*256+6, или 518. Этот же номер можно определить, выполняя системный вызов stat(2) для исходного файла /usr/foo. Номер устройства при этом появляется в поле st_dev. [Программа sta[ (21.1ц делает это автоматически. — JP] Итак, если вы, чтобы найти все файлы с номером индексного дескриптора 2076, выполните команду find / -inum 2076 -print, то можете получить больше трех файлов. Однако только три из них будут находиться в файловой системе sdOg. — СТ, из телеконференции net.unix в Usenet, 75 января 1985 г. 17.23 Поиск файлов с помощью оператора -prune Команда find имеет множество операторов для поиска файлов определенных видов. Причем команда не останавливается на текущем каталоге, а продолжает поиск и в подкаталогах, если таковые имеются. Как же в таком случае задать поиск только в текущем каталоге? Для этой цели предназначен оператор -prune. Примечание: Если ваша версия команды find не имеет оператора -prune, то описанные ниже приемы работать не будут. Попробуйте в таком случае использовать GNU-команду find, которая имеется на компакт-диске. Оператор -prune прекращает поиск, выполняемый командой find, на текущем путевом имени. Так, если текущее путевое имя принадлежит каталогу, то команда find не будет в него заходить для дальнейшего поиска. Командная строка при этом имеет достаточно сложный вид. Вот Команда find: поиск файлов 271 find
17.23 одна из таких строк, предназначенная для поиска всех файлов текущего каталога модифицированных в течение последних 24 часов: % find . \( -type d • -name . -prune \) -о \( -mtime -1 -print \) ./afile ./cfile Можете создать для этой команды псевдоним. Но сначала нужно увидеть ее в действии чтобы понять некоторые важные вещи (s.os, 17.12), остающиеся для многих неясными. Давайте проследим за работой команды. Команда find просматривает в текущем каталоге (.) каждую запись по очереди. При этом производится сравнение (в направлении слева направо) каждой записи с выражением заданным с помощью операторов. Если какая-либо часть выражения даст совпадение остальные части будут проигнорированы. 1. Команда find, рассматривая файл с именем ./afile, убеждается, что первая часть выражения \( -type d ! -name . -prune \), не дает совпадения, поскольку ./afile не является каталогом, Поэтому команда переходит к следующей части выражения, расположенной после оператора -о (OR). Был ли файл ./afile модифицирован в течение последних суток? В данном случае был, поэтому оператор -print, значение которого всегда истинно, выводит путевое имя. 2. Далее анализируется файл ,/bfile. Как и на предыдущем этапе, первая часть выражения не соответствует шаблону. При сравнении второй части, \( -mtyme -1 -print \), оказывается, что время модификации составляет более одного дня. Следовательно, эта часть выражения ложна. Команда find не выполняет оператор -print. 3. Переходим к каталогу ,/adir. Первая часть выражения, \ ( -type d ! -name . -prune \), дает совпадение. Объясняется это тем, что ./adir является каталогом (-type d), его имя не . (! -name .), поэтому оператор -prune, значение которого всегда равно true, делает эту часть выражения истинной. В результате команда find пропускает данный каталог. Ваша команда find может также иметь опцию -maxdepth, предназначенную для определения максимального количества уровней подкаталогов, на которые следует опускаться. Например, команда find . -maxdepth 0 работает только в текущем каталоге. Давайте создадим для команды find с оператором -prune два псевдонима. Первый из них, названный find, (с точкой в конце), просто выводит имена найденных файлов с помощью оператора -print. Второй псевдоним, подобно команде Is -gilds, выводит развернутый список имен. Работают они следующим образом: % find, -mtime -l ./afile ./cfile % find.Is -mtime -1 43073 0 -r 1 jerry ora 0 Mar 27 18:16 ./afile 43139 2 -r--r--r— 1 jerry ora 1025 Mar 24 02:33 ./cfile Псевдоним find, удобнее всего применять между обратными кавычками (9.щ, для вывода в канал, а также в других случаях, когда нужно получить список имен файлов. Приведем определения этих псевдонимов. Второй псевдоним, find.Is, вместо оператора -print использует оператор -Is. alias find, 'find . \( -type d ! -name . -prune \) -o \( \!* -print \)' alias find.Is 'find . \( -type d ! -name . -prune \) -o \( \!* -Is \)' (Версии для интерпретатора Bourne shell на компакт-диске называются Find и Findls, так как cshjnit точка в именах функций не допустима.) Если вам не хочется видеть в начале каждого имени символы ./, добавьте в конец всех определений оператор канала и команду colrm 1 2 (35.1s) или cut -сЗ - (З5.щ. -JP 272 Часть третья. Работа с файловой системой
17.25 17.24 Пропуск некоторых участков дерева каталогов Вопрос. Я хочу запустить команду find таким образом, чтобы стандартные каталоги типа /usr/spool и /usr/local/bin пропускались. Выражение -name каталог -prune не обеспечивает такой возможности, поскольку оператор -name сравнивает не все путевое имя, а только его часть, например spool или local. Как заставить команду find при сравнении пропускать конкретное путевое имя, такое как /usr/local/bin, а не сравнивать все каталоги bin? Ответ. Напрямую вы этого не сделаете, но можно задать команду test 44.20 find /путь -exec test {} = /foo/bar -о { } = /foo/baz \; -prune -о оператор Данная команда не будет выполнять оператор над каталогами /foo/bar и /foo/baz. Если нужно, чтобы каталоги входили в зону поиска, а их содержимое — нет, попробуйте задать: find /путь \( -exec test выражение \; ! -prune \) -о оператор Вторая версия заслуживает внимательного изучения с руководством по команде find в руках. Здесь продемонстрированы многие принципы работы команды. Оператор -prune просто говорит, что не нужно искать глубже в текущем пути, а далее поступает подобно оператору -print. Вопрос. Я хочу получить только список путевых имен. Тогда оператор, используемый в приведенном выше примере, будет оператором -print. Смогу ли я решить данную задачу, пропустив вывод команды find через фильтр sec[ (27.15) или egrep -v (27.03), который удаляет нежелательные путевые имена? Ответ. Этот способ, надо полагать, является самым быстрым. Использование команды test требует запуска программы test для каждого имени файла, что отрицательно сказывается на скорости работы. Дополнительные сведения о сложных выражениях команды find можно найти в другом месте, в частности в параграфах 17.06 и 17.12. - СТ, JP 17.25 Недопустимость поиска в сетевых файловых системах Самой трудной задачей при работе с сетевыми файловыми системами (NFS — Network File System) (1.зз) является предотвращение доступа к файлам, находящимся на выключенных серверах NFS. Это особенно важно при использовании команды find, которая в процессе выполнения может обращаться к десяткам машин. Если команда find попытается исследовать файловый сервер, оказавшийся выключенным, то ее работа прекратится только по истечении допустимого времени ожидания. Важно знать, как предотвратить доступ команды find к сетевым файловым системам. Чтобы это сделать, используйте оператор -prune совместно с оператором -fstype или -xdev. [К сожалению, не все версии команды find имеют эти операторы. — JP\ Оператор -fstype проверяет тип файловой системы, ожидая появления аргумента типа nfs или 4.2. Последний аргумент соответствует "быстрой файловой системе", появившейся в Berkeley Software Distribution версии 4.2. Чтобы ограничить поиск файлов, выполняемый командой find, только локальным диском (или дисками), используйте выражение -fstype 4.2 -prune или -fstype nfs -prune. При необходимости ограничить поиск одним разделом диска можно воспользоваться оператором -xdev. Например, если вы хотите очистить переполненный раздел диска, то все файлы текущего раздела размером более 40 блоков можно найти с помощью следующей команды: % find . -size +40 -xdev -print - вв Команда find: поиск файлов ■*■> 273
18 Создание ссылок, переименование и копирование файлов 18.01 Что сложного в процессе копирования файлов Трудно себе представить, что можно найти достаточно материала для написания целой главы о ссылках, перемещении и копировании файлов, но мы его нашли. К тому же есть несколько аспектов, делающих данную тему более сложной и более интересной, чем можно было ожидать. • UNIX-системы позволяют не только перемещать и копировать файлы, но и связывать их, создавая два имени файла (возможно, в разных каталогах и даже в разных файловых системах), которые указывают на один и тот же файл. Мы поговорим о том, зачем это нужно (параграф 18.03), какая разница между так называемыми жесткими и символическими ссылками (параграф 18.04), как их создавать (параграф 18.05), а также о многих проблемах, которые могут возникнуть при использовании ссылок (параграфы 18.06, 18.07 и 18.08). • Переименовать одновременно несколько файлов — задача нетривиальная, но UNIX, как правило, предоставляет достаточно возможностей избежать скучной процедуры переименования файлов по одному. Мы покажем много различных способов решения этой задачи, а заодно изучим разнообразные инструментальные средства UNIX. • В иерархических файловых системах приходится сталкиваться с проблемой перемещения не только файлов, но и целых иерархий каталогов из одного места в другое. В параграфах 18.15 и 18.16 описаны два способа достижения такой цели. Это обсуждение затрагивает область, относящуюся к последующим двум главам. Глава 19 посвящена "архивам" — большим файлам, содержащим множество других файлов и каталогов вместе с инструкциями по их воссозданию в первоначальном виде. В главе 20 рассмотрены принципы резервного копирования, посредством которого файлы архивов копируются на ленту. - TOR 18.02 Что на самом деле хранится в каталоге Для того чтобы изучить механизм перемещения и копирования файлов, сначала нужно понять, как файлы представлены в каталогах. Что означает выражение "файл находится в каталоге"? Легко представить, что файлы находятся внутри чего-либо (скажем, некоторого особого участка диска, называемого каталогом). Но в нашем случае модель файловой системы в виде шкафа для документов (i.iy не приемлема. Каталог — это, на самом деле, просто файл, который ничем не отличается от любого другого файла данных. Если хотите в этом убедиться, попробуйте выполнить команду od -с .; в большинстве систем UNIX она выводит содержимое текущего каталога в необработанном виде. Конечно, это будет выглядеть ужасно: файл не текстовый, в нем есть много двоичных кодов. Но если ваша система это допускает, то команда od -с (25.07) позволит увидеть имена файлов, содержащихся в текущем каталоге [и, возможно, некоторые имена удаленных файлов. 274 Часть третья. Работа с файловой системой
18.02 К сожалению, это просто старые записи в каталоге, и восстановить их невозможно (гз.м). — JP\. Если команда od -с не работает, используйте вместо нее команду Is -if. Итак, каталог является просто списком файлов. Он содержит имена файлов и номера индексных дескрипторов (/.22). Это значит, что визуализировать каталог можно примерно так: The file named . is inode 3434 6 The file named .. is inode 987 The file named mr.ed is inode 10674 The file named joe.txt is inode 8767 The file named grok is inode 67871 The file named otherdir is inode 2345 Когда, например, задается имя файла grok, ядро ищет слово grok в текущем каталоге и определяет, что этому файлу соответствует индексный дескриптор с номером 67871. Теперь можно найти этот индексный дескриптор и определить, кому принадлежит данный файл, где расположены его блоки данных и т.д. Более того, некоторые из таких "файлов" могут быть полноправными каталогами. В частности, сказанное относится к первым двум записям: . и ... Подобные записи есть в каждом каталоге. Одиночная точка (.) указывает на текущий каталог, а двойная (. .) — ма "родительский", т.е. на такой, что содержит текущий. Файл otherdir является еще одним каталогом, который оказался в текущем каталоге. Но способа определить все это из записей в каталоге не существует. UNIX их не различает, пока не заглянет в индексный дескриптор. Теперь, когда вы знаете, что такое каталог, подумаем о некоторых элементарных операциях. Что значит "переместить" или "переименовать" файл? Если файл остается в том же каталоге, команда mv меняет только имя в каталоге, а данные вообще не затрагиваются. Перемещение файла в другой каталог предполагает выполнение несколько большего объема работы. Скажем, команда mv dirl/foo dir2/foo означает "удалить запись foo в каталоге dirl и создать новую запись для файла foo в каталоге dir2". UNIX опять-таки не касается блоков данных или индексных дескрипторов. Единственным случаем, когда действительно необходимо копировать данные, является перемещение файла в другую файловую систему. При этом файл копируется в новую файловую систему, его запись в старом каталоге удаляется, блоки данных файла возвращаются в "список свободных" (последнее означает, что их можно использовать повторно) и т.д. Описанная операция довольно сложная, и сталкиваться с ней приходится относительно редко. (В некоторых старых версиях UNIX команда mv не позволяет перемещать файлы между файловыми системами.) А теперь проверим, все ли вы поняли из сказанного. Как UNIX определяет имя текущего каталога? В нашем "текущем каталоге" есть запись ., из которой видно, что текущему каталогу соответствует индексный дескриптор с номером 34346. Содержится ли имя текущего каталога в индексном дескрипторе? Конечно, нет. Имя данного каталога хранится в родительском каталоге. Родительский каталог имеет запись .., которой соответствует индексный дескриптор с номером 987. Поэтому UNIX ищет индексный дескриптор с этим номером, определяет, где находятся данные, и начинает читать все записи в родительском каталоге. Рано или поздно UNIX обнаружит запись, соответствующую индексному дескриптору с номером 34346. Когда это произойдет, система будет знать, что она нашла запись, относящуюся к текущему каталогу, и что можно прочесть его имя. Дополнительные сведения по этому вопросу и рисунок, иллюстрирующий сказанное, представлены в параграфе 14.04. Сложно? Да, но если вы это поймете, значит, получите представление о том, как работать с каталогами UNIX. - ML Создание ссыпок, переименование и копирование файлов 275
18.03 18.03 Файлы с двумя или несколькими именами Мы несколько раз затронем тему жестких и символических ссылкок оа.м, is.os, 24.04). Однако пока не было сказано ни слова о том, зачем на самом деле нужны файлы с несколькими именами. Когда я изучал UNIX, этот вопрос был настоящим камнем преткновения. Понять, что делают ссылки, просто, но зачем они нужны? Со временем я понял, что бывают ситуации, когда работать можно лишь со ссылками. Как только вы столкнетесь с проблемами, которые можно разрешить с помощью ссылок, вы начнете замечать и другие ситуации, когда таковые можно применять. Рассмотрим в качестве примера список телефонных номеров компании в системе, где работают несколько пользователей. Каждый пользователь хотел бы иметь копию такого списка. Однако предоставлять всем пользователям такие списки нецелесообразно. Во-первых, потому, что будет тратиться дисковое пространстю, а во-вторых, обновлять все индивидуальные списки при изменении телефонных номеров затруднительно. Одним из способов решения проблемы является предоставление каждому пользователю ссылки на главный список телефонов. Вот еще пример. Представьте, что вы используете несколько различных систем, имеющих общий доступ к файлам посредством сетевой файловой системы (NFS) (из). В конце концов вам надоедает редактировать пять или шесть различных файлов .login и .cshrc (2.02) каждый раз, когда добавляется определение нового псевдонима или изменяются некоторые элементы в файле инициализации. Было бы неплохо, если бы один и тот же файл появлялся в каждом из начальных каталогов. Также не мешало бы предоставить нескольким системам доступ к одним и тем же файлам баз данных. Рассмотрим еще одну ситуацию. Предположим, у вас есть программа или сценарий, выполняющий несколько взаимосвязанных функций. Почему бы не реализовать их всех в виде одного исполняемого файла? Все, что должен сделать сценарий (или программа), — это проверить имя, под которыми они были вызваны, и действовать в соответствии с этим. Как это работает, показано в параграфе 45.13. В параграфах 8.08, 16.07 и 22.10 приведены сценарии, функционирующие различным образом в'зависимости от их текущего имени. И последний пример. Предположим, у вас есть несколько версий файла: текущая, которая время от времени изменяется, и одна или несколько более старых. В соответствии с соглашением, эти файлы должны иметь имя data.дата, где дата показывает, когда данный файл был создан. Например, могут быть файлы с именами data.jull, data.jul2, data.jul5 и т.д. Однако при доступе к последнему из таких файлов вовсе не обязательно определять дату. Можно просто создать ссылку (символическую или жесткую) с именем data.cur, которая всегда указывала бы на самый последний файл. Следующий сценарий запускает программу output, помещает данные в файл, помеченный текущей датой, и переустанавливает ссылку data.cur. #!/bin/sh +%h%dSl.I0 curfile=data.'date +%h%cT linkname=data.cur output > $curfile rm -f $linkname In -s $curfile $linkname Мне пришлось встретиться с аналогичной ситуацией при написании технических руководств для одной компании. Я ориентировался на два класса читателей: одни хотели обращаться к руководствам по названиям, а другие (хотите — верьте, хотите — нет) предпочитали использовать для этой цели учетные номера. Вместо того чтобы все время искать учетные номера, я создал систему ссылок, с помощью которой можно было оперативно просматривать руководства либо по именам, либо по учетным номерам. Например, для руководства с именем Programming и учетным номером 046-56-3343 я создал файл /manuals/byname/programming, а затем ссылку с именем /manuals/bynumber/046-56-3343: % cd /manual/bynumber ..1.21 % In -s .. /byname/programing 046-56-3343 .. . . 276 Часть третья, Работа с файловой системой
18,04 Иногда же бывает необходимо просто собрать разнообразные файлы в одном каталоге. Эти файлы могут находиться в различных местах, а объединить их нужно лишь на какое-то время, например для того, чтобы скопировать на ленту. Предположим, вам нужно создать архив на ленте, включающий страницы руководства из каталога /development/doc/man/man 1, руководство из /development/doc/product, исходные тексты из /src/ccode и набор исполняемых модулей из /release/68000/execs. Приведенный ниже сценарий создает в каталоге /tmp/tape ссылки на все эти каталоги, а затем создает на ленте tar-архив (2o.oi), который можно послать, скажем, клиенту или другу. Обратите внимание: опция h сообщает утилите tar, что нужно отслеживать символические ссылки и архивировать файл, на который указывает ссылка. В противном случае утилита tar создаст копию только самой символической ссылки. #!/bin/sh /imp21.02 mkdir /tmp/tape cd /tmp/tape rm -f manl product ccode execs In -s /development/doc/man/manl In -s /development/doc/product In -s /src/ccode In -s /release/68000/execs tar ch ./manl ./product ./ccode ./execs Это лишь "вершина айсберга". Ссылки обеспечивают элегантные решения многих проблем, в том числе управляют исходными текстами, создают структуру файловой системы и т.д. -ML 1804 0 ссылках UNIX предоставляет ссылки двух видов. • Жесткие ссылки. С помощью жесткой ссылки два имени файла (две записи в катало- ге (18.02)) указывают на один и тот же индексный дескриптор и один и тот же набор блоков данных. Все версии UNIX поддерживают жесткие ссылки, но их применение связано со следующими существенными Ограничениями: во-первых, жесткие ссылки не могут указывать на файлы, находящиеся за пределами файловой системы (т.е. оба имени файла должны быть в одной файловой системе), а во-вторых, нельзя создать жесткую ссылку на каталог (т.е. каталог должен иметь только одно имя).* Использование жестких ссылок имеет важное преимущество: ссылка является лишь еще одним именем исходного файла, поэтому она не занимает никакого дискового пространства (за исключением тех редких случаев, когда занят дополнительный дисковый блок в файле каталога). • Символические ссылки. Символическая ссылка связывает два различньгх файла. Один из них содержит сами данные, а другой просто хранит имя первого файла и служит "указателем". Именно этот указатель мы и называем ссылкой. Система знает, что если открывается ссылка, то нужно прочесть ее содержимое, а затем обратиться к файлу, в котором на самом деле хранятся нужные данные. Все системы Berkeley UNIX и System V.4 поддерживают символические ссылки. Символические ссылки гораздо гибче жестких. Они могут указывать на файлы, находящиеся за пределами файловых и даже компьютерных систем (при условии, что используется сетевая файловая система NFS или RFS). Можно также создать символическую ссылку на каталог. Символическая ссылка имеет собственный индексный дескриптор, а для ее хранения требуется небольшой объем дискового пространства. Конечно, вы не сможете обойтись без копий файлов. Они просто необходимы, особенно если пользователь хочет иметь "персональную" версию главного файла. Но не менее важно знать о ситуациях, когда ссылкам нужно отдавать предпочтение. Использование ссылок предполагает наличие только одного набора данных и многих различных имен, с помощью которых можно получить доступ к данным. Как создаются ссылки, рассказывается в параграфе 18.05. На самом деле каталог имеет как минимум два имени (см. последний раздел этого параграфа). Создание ссыпок, переименование и копирование файлов 277
18.04 Различия между жесткими и символическими ссыпками При работе с жесткими ссылками два имени файла эквивалентны в любом смысле. Можно удалить одно, не затрагивая другое. Система удаляет запись в каталоге, соответствующую одному имени, и оставляет нетронутыми блоки данных, которые были общими. Единственное что делает команда rm с индексным дескриптором, — это уменьшает на единицу значение его счетчика ссылок, который, как следует из названия, содержит информацию о количестве жестких ссылок на файл. Блоки данных удаляются только тогда, когда счетчик ссылок обнуляется, что свидетельствует об отсутствии записей в каталогах, указывающих на данный индексный дескриптор. Как найти жесткие ссылки на файл, можно узнать из параграфа 17.22 В случае символических ссылок два имени файла на самом деле соответствуют различному содержимому. Удаление ссылки с помощью команды гт оставляет исходный файл нетронутым. Однако удаление или переименование исходного файла приводит к исчезновению имени и содержимого файла. У вас остается ссылка, которая указывает на несуществующий файл. (В параграфе 16.28 есть сценарий, предназначенный для обнаружения "висящих" символических ссылок.) Запомните, что сама по себе ссылка не имеет связанных с ней данных. Несмотря на такой недостаток, в версиях UNIX, поддерживающих символические ссылки, редко можно встретить жесткие ссылки. Благодаря своей гибкости символические ссылки становятся общепринятыми. В завершение рассмотрим список содержимого каталога, полученный с помощью команды Is. В этом каталоге есть файл file с жесткой ссылкой на него, имя которой — hardlink. Здесь также есть символическая ссылка на file, названная (как бы вы думали?) symlink. $ Is -lai total 8 140330 drwxr-xr-x 2 jerry ora 1024 Aug 18 10:11 . 85523 drwxr-xr-x 4 jerry ora 1024 Aug 18 10:47 .. 140331 -rw-r—r— 2 jerry ora 2764 Aug 18 10:11 file 140331 -rw-r—r— 2 jerry ora 2764 Aug 18 10:11 hardlink 140332 lrwxrwxrwx 1 jerry ora 4 Aug 18 10:11 symlink -> file Вы узнали опцию -/ (22.02) и, возможно, опцию -а (i6.il), предназначенную для включения в список файлов с точкой. Опция -/' выводит для каждой записи -в каталоге (is.02) номер индексного дескриптора (1.22), указанный в первом столбце. Третья колонка содержит значения счетчиков ссылок, т.е. данные о количестве жестких ссылок на файл. Если сравнить записи для file и hardlink, то можно заметить, что значения их счетчиков ссылок равны 2. В данном случае обе ссылки находятся в одном каталоге. Любые другие параметры (номер индексного дескриптора, размер файла, имя владельца и т.д.) для file и hardlink одинаковы. Объясняется это тем, что оба имени относятся к одному и тому же файлу, имеющему две ссылки (имени). В поле прав доступа для символической ссылки первым стоит символ 1. Номер ее индексного дескриптора не равен номеру дескриптора файла, на который она указывает, поскольку символической ссылке соответствует отдельный индексный дескриптор. Поэтому она, в отличие от жесткой ссылки, занимает дисковое пространство. Имя состоит из двух частей: имени символической ссылки (здесь symlink) с последующей стрелкой и имени, на которое ссылка указывает (в данном случае file). Размер файла symlink равен всего 4 байтам, но этого вполне достаточно для хранения путевого имени (file), на которое ссылка указывает. Ссылки на катапоги По ходу дела предлагаем вашему вниманию параграф, который не относится к созданию ссылок на файлы или символических ссылок. Рассмотрим первые две записи из предыдущего списка содержимого каталога, обратив внимание на ссылки и значения счетчиков ссылок. Это поможет связать воедино файловую систему (как в прямом смысле, так и в вашем сознании). Вы, конечно же, встречали записи . и .. в путевых именах (i.2i) и, возможно, прочитали о том, что находится в каталоге (1Ш). Запись . является ссылкой на текущий каталог. Обратите 278 Часть третья. Работа с файловой системой
18.05 внимание, что значение счетчика ссылок равно 2. Где же вторая ссылка? Она находится в родительском каталоге: $ is -li ... total 2 140330 drwxr-xr-x 2 jerry ora 1024 Aug 18 10:11 sub 85524 drwxr-xr-x 2 jerry ora 1024 Aug 18 10:47 sub2 Обратите внимание на номера индексных дескрипторов в записях родительского каталога. Какая запись соответствует нашему текущему каталогу? Запись для sub имеет номер 140330, такой же, как запись . в списке текущего каталога, Поэтому текущий каталог называется sub. Теперь вам должно быть понятно, почему на каждый каталог имеется не менее двух ссылок. Одна ссылка, с именем ., указывает на сам каталог. Другая ссылка, в родительском каталоге, задает имя каталога. (В параграфе 14.04 все сказанное представлено на рисунке.) В каждом каталоге есть запись . ., которая является ссылкой на его родительский каталог. Если взглянуть на список текущего каталога, то можно заметить, что родительский каталог имеет четыре ссылки. Где они? Постарайтесь ответить на этот вопрос самостоятельно. (Чур, вперед не заглядывать!) Дззззззз... Время истекло! Если каталог имеет подкаталоги, значит, на него имеются жесткие ссылки с именами . . в каждом подкаталоге. Из представленного выше вывода команды Is -li . . видно, что к родительскому каталогу относятся два подкаталога: sub и sub2. Это две ссылки из четырех. Двумя другими ссылками являются запись . в родительском каталоге и запись для родительского каталога (имеющего имя test) в его родительском каталоге: -d 16.08 $ Is -dli . . / . . . / . . /test 85523 drwxr-xr-x 4 jerry ora 1024 Aug 18 10:47 ../. 85523 drwxr-xr-x 4 jerry ora 1024 Aug 18 10:47 ../../test Как и должно быть, все ссылки имеют одинаковый номер индексного дескриптора — 85523. Улавливаете? Эта концепция может показаться несколько абстрактной, а поначалу даже трудной для понимания. Однако вы должны в этом разобраться, особенно если являетесь системным администратором, использующим такое сильное средство, как команда clri озлз). В качестве упражнения можете создать подкаталог и поэкспериментировать с ним, как описано в этом параграфе. Между прочим, каталоги и их жесткие ссылки . и . . создаются с помощью системного вызова mkdlr(2). Это единственный для обычного пользователя способ создания каталога (и ссылок на него). Дополнительные сведения по этой теме приведены в параграфе 23.18. - JP, ML 18.05 Создание и удаление ссылок Команда In создает как жесткие, так и символические ссылки as.M). Однако только в версиях UNIX, поддерживающих символические ссылки, в команде In имеется опция -s: % In файл ссылка Для создания жесткой ссылки % In -s файл ссылка Для создания символической ссылки В обоих случаях файл уже должен существовать, иначе вы получите сообщение об ошибке. В Berkeley UNIX сообщение об ошибке будет выдано, если файл ссылка уже имеется; в System V такой файл может существовать. Если вы имеете право модифицировать файл, то команда /я разрушает его старое содержимое и создает ссылку; если права записи в файл ссылка не существует, то команда /и запрашивает разрешение на игнорирование защиты файла. Например: % In foo bar In: override protection 444 for bar? у Создание ссыпок, переименование и копирование файлов 279
18.06 Ответ у позволяет команде In уничтожить файл bar и создать ссылку. Обратите внимание что сделать это не удастся, если у вас нет права записи в каталог. В команде In аргумент ссылка можно опустить. В этом случае в качестве имени ссылка берется последний компонент имени файл (т.е. все, что находится после последней косой черты). Конечно, это предполагает, что ссылка не указывает на текущий каталог. Если же она указывает на таковой, то команда не выполнится, поскольку ссылка уже существует. Две следующие команды являются эквивалентными: .1.21 % In -s . . /archive/file. с % In -s ../archive/file.с file.с Обе команды создают' в текущем каталоге ссылку ftle.c на файл ../anhwe/fllex. Команда In позволяет также с помощью одной команды создать группу ссылок, правда, при условии, что все ссылки будут находиться в одном каталоге. Вот как это делается: % In файл1 фяйл2 файлЗ ... каталог Данная команда использует имя файла (без имени каталога) в качестве имени соответствующей ссылки. Затем она создает все ссылки в указанном каталоге. Например, первая из приведенных ниже команд эквивалентна двум следующим: % In ../s/f1 ../s/f2 current % In ../s/f1 current/f1 % In ../s/f2 current/f2 Этот перечень файлов можно заменить шаблоном с метасимволами (п.огу. .1.21 % In -s . . /newvergion/*. [ch] . He забывайте, что символические ссылки могут устареть as.ot). [Жесткие ссылки в определенных ситуациях также могут "оборваться". Так, текстовый редактор может переименовать ссылку с именем textfile в -textfile.bak, а затем при редактировании создать новый файл textfile. Но другие оставшиеся ссылки теперь будут указывать на файл textfile.bak, а не textfile. Чтобы разобраться в этом, найдите ссылки на каждый файл (П.22). — JF] Для удаления ссылки — либо жесткой, либо символической — используйте команду гт. - ML 18.06 Устаревшие символические ссылки При использовании символических ссылок возникает одна проблема: они довольно быстро устаревают. Что это значит? Рассмотрим следующие команды: % In -s foo bar % rm foo Что произойдет, если их выполнить? Как вы помните, ссылка bar служит указателем. Сама по себе она не содержит никаких реальных данных. Ее данные — это имя файла foo. После удаления файла foo ссылка бал продолжает существовать, хотя и указывает на несуществующий файл. Команды, обращающиеся к файлу bar, будут выдавать неприятное сообщение: % cat bar cat: bar: No such file or directoty Его появление может вас сильно огорчить. Команда Is покажет, что файл bar по-прежнему существует. Вы ничего не сможете понять, пока не осознаете, что bar — всего лишь указатель на файл, которого больше не существует. [Команды Is -LI и Is -LF позволяют обнаружить "висящие" символические ссылки. Опция -L означает, что необходимо вывести имя файла, на который указывает эта ссылка, а не имя ссылки. Если ссылка указывает на несуществующий файл, то команда Is -L выведет имя ссылки. — JF\. 280 Часть третья. Работа с файловой системой
18:07 Существует множество безобидных причин возникновения недействительных символических ссылок. Таковыми, в частности, могут служить переименование файла данных foo, перемещение файла foo или bar (а возможно, и обоих) в другую часть файловой системы, где указатель будет недействительным. Один из способов избежать проблем, связанных с недействительными ссылками, — использование относительных путевых имен (i.2i). Так, применение относительных путевых имен позволяет перемещать все дерево каталогов без разрушения ссылок (при условии, что и файл, и ссылка размещены в этих каталогах). Приведем пример. Предположим, что у вас есть файл /home/mars/john/project/datastash/inputl23.txt и вы хотите создать для него ссылку /home/mars/john/test/input.txt. Чтобы сделать это, воспользуемся такими командами: % cd /home/mars/john/test % In -s ../project/datastash/inputl23.txt input.txt Co временем вы разрешаете воспользоваться вашими файлами Мэри, и она копирует os.it) целиком каталоги project и test в свой начальный каталог. Ссылка input.txt на файл inputl23.txt по-прежнему будет действительной. Хотя оба файла переместились, "взаимоотношения" между ними (т.е. относительный путь из одного каталога в другой) остались прежними. Рассмотрим другой пример. Предположим, что вас пересадили за другой компьютер, с именем Jupiter, и при переходе на него вы скопировали весь свой начальный каталог. В данном случае ссылки также остаются действительными: относительный путь из каталога test в каталог datastash не изменился, несмотря на то, что полные путевые имена обоих каталогов различны. С другой стороны, иногда бывает уместным использовать полные путевые имена (14.02). Они особенно полезны при необходимости переместить только ссылку, а не исходный файл. Допустим, вы создаете ссылку из своего рабочего каталога на файл в главном каталоге проекта (скажем, /corp/masterdata/input345.txf). Вполне вероятно, что вы захотите реорганизовать свой рабочий каталог до того, как кто-то начнет перемещать главный набор файлов проекта. В этом случае ссылку можно создать следующим образом: % In -s /corp/masterdata/input345.txt input.txt Теперь ссылку input.txt можно перемещать по файловой системе куда угодно и она будет действительной, но только при условии, что файл input345.txt остается на месте. В параграфе 16.28 приводится сценарий, предназначенный для поиска устаревших символических ссылок. Отметим, что при использовании жестких ссылок (is.M) подобные проблемы никогда не возникают. Причем в этом случае никакой разницы между ссылкой и оригиналом не существует. По сути, называть одно ссылкой, а другое файлом — это даже некорректно. - ML 18.07 Ссылки на каталоги Одна из характерных особенностей символических ссылок (is.04) заключается в том, что они, в противоположность жестким ссылкам, могут указывать не только на файлы, но и на каталоги. Более того, символические ссылки действуют и между файловыми системами, что также можно отнести к их преимуществам. Бывает, что системные администраторы инсталлируют пакеты в таком месте файловой системы, где пользователи и другие программы не ожидают их встретить. На своем узле мы предпочитаем держать каталог /usr/bin чистым — в таком случае можно быть уверенным, что все программы в этот каталог поступили вместе с операционной системой. Тогда при инсталляции новой операционной системы можно полностью переписать каталог /usr/bin, не опасаясь потерять какие-нибудь локальные программы. Все локальные программы содержатся в каталоге /usr/local. Однако использование пакета XII (Ui) может вызвать проблему. Программы этого пакета рассчитаны на инсталляцию в каталоге /usr/bin/Xll. Но пакет XI1 не распространяется как часть ОС, поэтому мы бы предпочли не помещать его туда. Есть смысл инсталлировать программы XII в каталог /usr/local/Xl 1/Ып и создать символическую ссылку с именем Создание ссыпок, переименование и копирование файпов 2Ы
18.08 /usr/bin/Xll. Аналогичным образом следует поступить с каталогами /usr/include/XlI и /usr/lib/Xll. # In -s /usr/local/Xll/bin /usr/bin/Xll # In -s /usr/local/Xll/lib /usr/lib/Xll # In -s /usr/local/Xll/include /usr/include/Xll Благодаря использованию символических ссылок мы достигли двух целей: инсталлировали пакет там, где хотели, и сделали его невидимым для пользователей и программ, которые рассчитывают увидеть программы, библиотеки и файлы заголовков в стандартных каталогах Однако использование ссылок на каталоги может привести к неожиданностям. Предположим мне нужно просмотреть файлы в каталоге /usr/bin/Xll. Я пытаюсь перейти в каталог /usr/bin/Xll, не подозревая о том, что файлы на самом деле находятся в /usr/local/Xll/bin: % cd /usr/bin/Xll -F 16.12 % Is -F mkfondir* xcalc* xcalc* xinit* xset* Но если выполнить команду pwd, то станет ясно, что в действительности я нахожусь в каталоге /usr/local/Xll/bin. Если бы я не знал о существовании символической ссылки, это могло бы меня смутить: % pwd /usr/local/Xll/bin А теперь предположим, что я хочу просмотреть файлы в каталоге /usr/bin. Поскольку я выполнил команду cd для перехода в каталог /usr/bin/Xll, то могу подумать, что нужно подняться на уровень выше. Но это не так: % cd .. % Is -F bin/ include/ lib/ % pwd /usr/local/Xll Что произошло? Вспомните, что символическая ссылка является просто указателем на другой файл или каталог. Поэтому после перехода в каталог /usr/bin/Xll моим текущим рабочим каталогом стал /usr/local/Xll/bin, на который указывает ссылка /usr/bin/Xll. И Способы решения этой и других проблем предоставляет программа Indir, поставляемая с пакетом XII. (Она также имеется на нашем компакт-диске.) Программа Indir создает символические ссылки между каталогами путем организации ссылок на каждый отдельный 1пфг файл. Это примитивно, но работает. Мы можем использовать программу Indir и вместо команды In -s: # Indir /usr/local/Xll/bin /usr/bin/Xll # Is -F /usr/bin/Xll X@ mkfontdir@ xcalc@ xinit@ xset@ - LM 18.08 Вывод имен файлов, соответствующих символическим ссылкам Программа si является сценарием языка Perl (37.01). Она просматривает все указанные в (#1 ] командной строке путевые имена и сообщает для каждого из них, пришлось ли ей пройти по символической ссылке и найти настоящее имя файла. Символические ссылки на полные путевые имена начинаются без отступа; символические ссылки на относительные путевые имена размещаются под именами файлов и каталогов, на которые они указывают. Например: $ si /usr/lib/libXw.a 282 Часть третья. Работа с файловой системен
18.10 /usr/lib/libXw.a: /usr/lib/libXw.a -> /usr/lib/Xll/libXw.a /usr/lib/Xll -> /Xll/lib /Xll -> /usr/local/XHR4 /usr/local/XHR4/lib/libXw.a $ si /bin/rnews /bin -> /usr/biniousr/lib/news/rnews /usr/lib/news -> ../local/lib/news local/lib/news/rnews -> inews inews — LW, RS, из книги Programming Perl издательства O'Reilly & Associates 18.09 Переименование, копирование и сравнение групп файлов Если вы попытаетесь переименовать группу файлов с именами, заканчивающимися на . new, таким образом, чтобы они заканчивались на .old, то команда % mv *.new *.old Неправильно! работать не будет, поскольку интерпретатор shell не может найти соответствия шаблону * .old (i.is), а также потому, что команда mv просто никогда не работает таким образом. Вот как это можно сделать по-другому: -d 16.08 % Is -d *.new I sad "s/\ (. *\) \ .new$/mv 'S' '\l.old'/" I sh \(..\)..\l 34.10 Здесь для каждого файла создается команда mv, которая передается интерпретатору shell. Использование обратной косой черты и кавычек гарантирует сохранение специальных символов (8.19) нетронутыми. Это не всегда нужно делать, но такой прием просто необходим, если вы не знаете, какие файлы будут переименовываться: mv 'afile.new' 'afile.old' mv 'bfile.new' 'bfile.old' (Если вы хотите только посмотреть, как команды будут генерироваться, удалите выражение I sh или используйте команду sh -v (s.iy.) Чтобы выполнить копирование, замените команду mv командой ср. Для безопасности используйте команду mv -i или ср -i, если ваши версии этих команд поддерживают опцию -/ (2i.ii). Этот метод работает с любыми командами UNIX, принимающими в качестве аргументов по два имени файла. При необходимости фавнить, скажем, группу файлов в текущем каталоге с исходными файлами в каталоге /usr/local/src используйте команду diff (is.oi/. % Is -d *.c *.h | sad 'a@.*@diff -c & /uar/local/src/S@' | sh - JP 18.10 Переименование можно осуществить разными способами [В параграфе 18.09 Джерри Пик рассказал, как, воспользовавшись некоторым стандартным шаблоном, можно сконструировать пользовательскую команду для замены одного набора имен файлов другим (правда, для этого необходимо иметь четкое представление о работе интерпретатора shell и такой утилиты, как sed). В параграфе 18.11 Линда Мюи (Linda Mui) объясняет программу на С, которая облегчает указанную операцию и содержит некоторые встроенные средства защиты, предотвращающие любые переименования, если при этом могут возникнуть проблемы. В данном параграфе Ларри Уош (Larry Wall) и Рэндал Шварц (Randal Schwartz) представляют сценарий Perl (Jzoi), имеющий дополнительные возможности. Между прочим, девизом языка Perl является следующий: "Это можно сделать разными способами". — TOR] издание ссыпок, переименование и копирование фатов 283
18.11 В UNIX имеется несколько способов переименования групп файлов, большинство из которых ^ ' являются "неофициальными". Их применение предполагает, что вы будете использовать специальные преобразования переменных или запускать по нескольку процессов. С помощью '™™ Perl-сценария rename можно переименовать файлы в соответствии с правилом, заданным первым аргументом. Этот аргумент является просто выражением языка Perl, которое должно модифицировать строку $_ в сценарии [текущую строку ввода — ТОЛ], по крайней мере, для некоторых из указанных имен файлов. Так, файлы можно переименовать с помощью записи, подобной sl/l (34.24), с которой вы, надо полагать, уже знакомы. Если какое-либо имя файла не модифицируется выражением, то файл не будет переименован. Если в командной строке не задано ни одного имени файла, то имена будут читаться из стандартного ввода. Например, чтобы переименовать все соответствующие шаблону *.bak файлы путем удаления расширения имени, необходимо выполнить такую команду: % rename 's/\.bak$//' *.bak Однако не стоит ограничивать свои действия простыми подстановками — в вашем распоряжении вся мощь выражений языка Perl. Например, чтобы вернуть прежнее расширение имен, выполните команду % rename '$_ .= ".bak"" * или даже % rename 's/$/.bak/' * Для замены прописных букв строчными воспользуйтесь командой % rename 'tr/A-Z/a-z/' * А как насчет такого: % rename 's/foo/bar/; $_ = $was if -e1 *foo* % find . -print | rename 's/readme/README/i' % find . -print | rename 's/$/.old/ if -M $_ > 0.5' % find . -name '*,v' -print | \ rename ■s#(.*)/#$l/RCS/#, $x{$l>++ || mkdir("$1/RCS", 0777)' [Конечно, чтобы понять эти, а также более сложные конструкции, вам нужно поближе познакомиться с языком Perl. Поверьте, вы Не пожалеете о времени, затраченном на его изучение. — ТОЩ — LW, RS, из книги Programming Perl издательства O'Reilly & Associates 18.11 Переименование файлов с помощью команды ген На компакт-диске содержится команда с именем геп, которую также можно использовать для переименования группы файлов. Основное преимущество этой команды — ее гибкость. Допустим, у меня есть файлы PostScript с именами ps.ch01, ps.ch02 и т.д. Мне же нужно, чтобы эти файлы имели имена, соответствующие общепринятому соглашению: chOl.ps, chOZps, ch03.ps и т.д. Такое переименование можно осуществить с помощью, простого сценария, но гораздо проще воспользоваться командой геп. •^^И Команда геп распознает специальные символы * и ? и использует каждый подставленный Г (9) i вместо них набор символов в шаблоне замены. Первый набор символов, подставленный в ^__^ имя файла вместо метасимвола, обозначается как аргумент номер 1 (#1), второй — как геп аргумент номер 2 (#2) и т.д. Проще всего это объяснить, продемонстрировав применение команды геп. % Is ps.chOl ps.ch02 ps.ch03 ps.ch04 ps.ch05 ps.ch06 ps.ch07 284 Часть третья. Работа с файловой систем*
18.11 Используйте в строке поиска метасимвол *, а затем в том месте, куда нужно вставить соответствующую этому символу строку, поставьте в строке замены #1. Поскольку звездочка является специальным символом, который должен интерпретироваться командой геп, а не интерпретатором shell, то ее нужно защитить кавычками (s.n). В Bourne shell символ # служит символом комментария — его также необходимо взять в кавычки. Интерпретатор С shell, работающий в интерактивном режиме, не считает # символом комментария, однако для надежности мы взяли в кавычки и его: % ran "рэ.*" "#1.рэ" Если команда геп не выдала никаких сообщений, значит, все прошло нормально и файлы были переименованы. Вы можете убедиться в этом, осуществив повторный вывод списка каталога: % Is ch01.ps ch02.ps ch03.ps ch04.ps ch05.ps ch06.ps ch07.ps Команда геп не позволяет переписывать существующие файлы без вывода соответствующего предупреждения. Предположим, в нашем каталоге уже был файл с именем ch07.ps: % is ch07.ps ps.chOl ps.ch02 ps.ch03 ps.ch04 ps.ch05 ps.ch06 ps.ch07 При попытке переименовать файлы команда геп выдаст сообщение о том, что файл ch07.ps будет перезаписан: % геп "рэ.*" "#l.ps" ps.ch07 -> ch07.ps ; remove old ch07.ps? Вывод такого сообщения может быть отменен посредством опции -d, которая позволяет переписывать файлы без предупреждения. С ней связаны опция -к, запрещающая переписывание файлов и подавляющая вывод соответствующего предупреждения, а также опция -а, прекращающая всю процедуру, если какой-нибудь файл должен перезаписываться. В случае использования опции -а выполнение команды геп прекращается до переименования любого файла, поэтому все можно начать сначала. Команда геп также достаточно "разумна", чтобы обнаружить конфликты имен, прежде чем приступить к выполнению операции переименования файлов. Предположим, у нас есть файлы с префиксами ps. и eps., а мы хотим переименовать их, воспользовавшись суффиксом .ps. Если возникнет какой-либо конфликт, команда геп сразу предупредит нас об этом, и ни один файл не будет переименован: % Is README ps.chOl ps.ch03 ps.ch05 ps.ch07 eps.ch07 ps.ch02 ps.ch04 ps.ch06 % ran "*ps.*" "#2.ps" Two or more files would have to be renamed to 'ch07.ps'. Aborting, no renames done Команда геп имеет ограничение в том смысле, что ее можно использовать для переименования файлов только в пределах одного каталога. Хотя в некоторых приложениях это делать неудобно, но зато такая возможность обеспечивает большую степень безопасности. Чтобы продемонстрировать работу команды геп в более сложной ситуации, возьмем другой пример. Каждую неделю я пишу отчеты и сохраняю их под именами типа месяц.число.год. Со временем я заметил, что способ сортировки, используемый командой Is по умолчанию, не обеспечивает вывода файлов в хронологическом порядке. % Is 1.13.92 1.27.92 12.23.91 2.3.92 1.20.92 1.6.92 12.30.91 Создание ссылок, переименование и копирование файлов 285
18.12 Оказалось, что целесообразнее переименовать файлы в формате год.месяц.число и использовать ведущий ноль для представления первых девяти месяцев. Это несложно сделать с помощью двух команд геп: % геп "?.*.*" "#3.0#1.#2" % геп "1?.*.9?" "9#3.1#1.#2" % Is 91.12.23 92.01.13 92.01.27 92.02.3 91.12.30 92.01.20 92.01.6 Первая команда переименовывает все отчеты за месяцы 1—9. Вторая команда "отбрасывает" те файлы, которые уже были переименованы (я позаботился об этом). - LM 18.12 Переименование файлов в интерактивном режиме В параграфе 18.09 показано, как можно переименовать группу файлов, заменив имена *.new именами *.old. А теперь мы расскажем о другом способе переименования, который выполняется из редактора vi и предоставляет возможность предварительно просмотреть и отредактировать команды. Перечислим действия, которые нужно для этого произвести: % vi Запуск редактора vi без указания имени файла :r !Is *.nsw Получение списка файлов, по одному на строку &34.09 :%s/.*/mv & S/ Создание командной строки mv $26.04 : %s/new$/old/ Замена new именем old :w !sh Запуск команд путем передачи их интерпретатору shell :q! Выход из vi без сохранения Создав для команды Is псевдоним (\o.o2), изменяющий формат ее вывода, вы, скорее всего, столкнетесь с проблемой. Если этот псевдоним не выводит простой перечень файлов в один столбец, вместо команды !ls используйте !/bin/Is. - JP 18.13 Еще один способ переименования Нетрудно представить, что какой-то неопытный пользователь, не доверяющий себе даже при замене одного шаблона другим, не желает повторять длинный перечень команд mv. Приведем простой сценарий (i.os), который принимает список имен файлов (возможно, с метасимволами) и запрашивает у пользователя новое имя для каждого из них: #!/bin/sh # Использование: newname список_файлов for х do -п 8.06 echo -n "old name is $x, new name is: " read newname mv "$x" "$newname" done Вот пример использования данного сценария: touch 21.07 % touch junkl junk2 junk3 % nswname junk* old name is junkl, new name is: testl old name is junk2, new name is: test2 old name is junk3, new name is: test3 Этот сценарий очень прост, и в компакт-диск он не включен. Я привел его, желая показать, что переименование можно осуществить разными способами, даже не используя язык Perl (ИМ- - TOR 286 Часть третья. Рабом с файловой системой
18.15 Изменение объекта символических ссылок Как и программа rename, описанная в параграфе 18.10, программа relink (Perl-сценарий) изменяет объект символических ссылок в соответствии с правилом, задаваемым первым аргументом. Этим аргументом является выражение на языке Perl, которое модифицирует текущую строку ввода $_ в сценарии, по крайней мере, для некоторых из указанных имен файлов. Выражение, заданное первым аргументом, будет применено к содержимому символической ссылки, описанной в командной строке. Если содержимое данной символической ссылки не модифицируется выражением, то ссылка не изменяется. Имя, указанное в командной строке и не являющееся символической ссылкой, будет проигнорировано. Если в командной строке не указано ни одного имени, то информация будет читаться из стандартного ввода. Например, если необходимо изменить объект всех символических ссылок в текущем каталоге, указывающих на что-либо в каталоге X11R3, таким образом, чтобы они указывали на одноименные объекты в каталоге X11R4, можно выполнить следующую команду: $ relink 'S/X11R3/X11R4/' * Для того чтобы все ссылки системы указывали не на каталог /usr/spool, а на каталог /var/spool, введите -typel/Z/J $ find / -type 1 -print | relink ' s#A/usr/spool#/var/spool#' — LW, RS, из книги Programming Perl издательства O'Reilly & Associates 18.15 Копирование дерева каталогов с помощью команды ср -г Некоторые версии команды ср имеют опцию -г (рекурсия). С ее помощью копируются все файлы дерева каталогов, т.е. все файлы каталога и подкаталогов. Примечание; Одна из наших UNIX-систем имеет команду ср без опции -г. Но в ней есть также команда rep (U3), которая имеет опцию -г. Эта команда может копировать файлы на любую машину, не только на удаленную. Когда мне нужна команда ср -г, я использую гср -г. Первыми аргументами команды ср -г могут быть имена каталогов или файлов. Если задаются имена файлов, то они будут скопированы точно таким же образом, как и без опции -г. Последним аргументом должно быть имя каталога. Итак, команду ср -г можно использовать по-разному. При этом оба способа дают одинаковый результат. • Во-первых, посредством команды ср -г можно задавать имена каталогов, которые нужно скопировать. Они будут созданы как подкаталоги в каталоге, указанном в последнем аргументе. • Во-вторых, с помощью команды ср -г можно задавать имена файлов, которые нужно скопировать. Они будут скопированы в каталог, указанный последним. О том, как выполняется копирование, можно судить по рис. 18.1. Здесь копируется каталог /home/jane со всеми его файлами и подкаталогами и создается подкаталог jane в текущем каталоге (.) (i.2i/. % cd /work/bkup % ср -г /home/jane . Как скопировать содержимое подкаталога data и все его файлы (но не сам подкаталог) в дублирующий каталог data.bak? Сначала необходимо создать каталог назначения. Это связано с тем, что последним аргументом команды ср -гдолжно быть имя уже существующего каталога: % cd /home/jane % mkdir data.bak % ср -г data/* data.bak Создание ссылок, переименование и копирование файлов 287 18.14 relink
18.15 Данная команда не копирует скрытые файлы каталога data. Однако существует способ os.os) позволяющий делать это. Рис. 18.1. Копирование каталога /home/jane в текущий каталог (/work/bkup) с помощью команды ср Копирование подкаталогов Sep и Oct вместе с их файлами, а также файла Output из каталога /home/jim/calendar в текущий каталог (.) осуществляется так: IV НЮ % ср -г /home/jim/calendar/[SO]* . Если вы используете интерпретатор С shell или bash, то с помощью удобных операторов фигурных скобок (9.05) можно скопировать только каталоги: % ср -г /home/jim/calendar/{Sep,Oct}* . Перечислим некоторые особенности команды ср -г. • Символические и жесткие ссылки (is.04) копируются как файлы. Это, с одной стороны, очень хорошо, поскольку в новом месте символическая ссылка может указывать на несуществующий файл. С другой стороны, это несколько неудобно, особенно если ссылка указывает на очень большой файл и копия занимает много дискового пространства. (Обратите внимание на рис. 18.1, где символическая ссылка в начальном каталоге пользователя jane была преобразована в файл с именем .setup, хранящий копию содержимого файла generic.) • Во многих UNIX-системах копия файла датируется временем ее создания и может иметь права доступа, установленные командой umask (22.04). Если нужно, чтобы копия имела исходное время модификации и права доступа, добавьте опцию -р. 288 Часть третья. Работа с файловой системы
18.16 • Команда ср -г войдет в бесконечный цикл, если в качестве каталога-источника и каталога-приемника будет указан один и тот же каталог. Предположим, вы копируете все из текущего каталога в существующий подкаталог backup: % ср -г * backup Если ваша команда ср -г работает так же, как и используемая мною, то будут созданы подкаталоги backup/backup, backup/backup/backup и т.д. Чтобы избежать этого, замените метасимвол * другим, менее универсальным. Вы также можете обеспечить подстановку имен всех каталогов, кроме имени каталога назначения, воспользовавшись оператором ! в интерпретаторе ksh, оператором п в интерпретаторе tcsh (is.02) или сценарием пот (is.o9). - JP 18.16 Копирование дерева каталогов с помощью команды tar Команда tar (n.os) предназначена не только для создания ленточных архивов. Она также может копировать файлы с диска на диск. И даже если на вашем компьютере имеется команда ср -г, использование команды tar дает некоторые преимущества. Очевидный способ копирования каталогов с помощью команды tar состоит в том, чтобы записать их в архив на магнитной ленте с относительными путевыми именами, а затем восстановить с ленты и записать куда-нибудь на диск. Однако команда tar может также направлять данные в канал и читать из канала. Это выглядит примерно так: % чтение^аг-архива | зались_ tar-архива Один нюанс: процесс 3anucb_tar-apxuea и процесс чтениеjar-apxuea имеют разные текущие каталоги (за.оз, зя.М), т.е. их копии помещаются в разные места. Чтобы выполнить это условие, необходимо запустить порожденный интерпретатор shell (u.ot) и уже в нем осуществить операцию 3anucb_tar-apxuea. Аргументами процесса чтение Jar-apxuea могут быть имена каталогов или файлов. Не забудьте только использовать относительные путевые имена (И.о2), которые не начинаются с косой черты, иначе процесс 3anucb_tar-apxuea запишет копии в то же место, откуда поступили оригиналы! "А как насчет примера?" — спросите вы. Один из них приведен на рис. 18.2. Здесь копируются все файлы и подкаталоги каталога /home/jane; копия создается в каталоге /work/bkup/jane: % mkdir /work/bkup/jane % cd /home/jane % tar cf - . | (cd /work/bkup/jane ss tar xBf -) Оператор && (44М) заставляет интерпретатор shell запускать команду tar xBf только в том случае, если предыдущая команда (cd) была успешно выполнена. Это предотвращает запись командой tar файлов в тот же каталог, из которого они читаются. Такое может случиться, если каталог назначения недоступен или если вы исказили его путевое имя. Если ваша команда tar имеет опцию В, предназначенную для группировки блоков в одну запись, используйте ее, и вы получите гарантию того, что копия создана правильно. В случае отсутствия таковой можно применить следующий трюк, предложенный Крисом Тореком: % tar cf - . | cat | (cd /work/bkup/jane && tar xbf 1 -) По крайней мере одна версия команды tar имеет опцию v (описание), которая помещает описание в стандартный поток вывода, а не в стандартный поток ошибок (19.08)1 Помните, что данную опцию можно задействовать только в процессе 3anucb_tar-apxuea, и ни в коем случае не используйте ее в процессе чтение_tar-apxuea — процессе, выводящем данные в канал. Вы можете также использовать в процессе 4menuejar-apxuea другие опции, которые поддерживает ваша команда tar, например опцию исключения файлов или каталогов (2ом). Назовем некоторые особенности работы команды. Создание ссылок, переименование и копирование файлов Ю 9-171 289
18.16 Символическая ссылка ра.м) будет скопирована буквально. Если символические ссылки указывают на относительные путевые имена, то скопированные ссылки могут указывать на несуществующие файлы. Такие символические ссылки можно найти с помощью команд find -type I (17.13) или oldlinks (Н.28). Жесткая ссылка рам) будет скопирована как файл. Если среди копируемых файлов есть другие жесткие ссылки на этот же файл, то они будут ссылаться на копию, а не на исходный файл.* Это может' оказаться полезным, если копия будет располагаться в другой файловой системе (жесткая ссылка на исходный файл в таком случае не работает). Однако такое положение нежелательно, если ссылка указывает на очень большой файл, так как копия будет занимать много дискового пространства. Подобные ссылки можно найти: — путем выполнения в каталоге, из которого осуществляется копирование, команды Jmd -links +1 -type f (17.05), позволяющей обнаружить все файлы с более чем одной ссылкой; — посредством запуска процесса чтение tar-apxuea с опцией /, выводящей сообщение если не все ссылки скопированы в файл. I work | - Каталог 1.!?.ЛЛ.j ~ Текущий каталог ("аэкш; - Символическая ссылка ( 1 ) -Файл home Jim 1 Jane calendar data Aug Sep [Oct | ( Output ) | Sub Ct) ПП (с) JlL i X.— 0 00 0 I000 © 0 work setups I .setup щШШтг*'-'- (generic)! bkup jane [I setup J I data I ! H"- 000] ! 000 I Рис. 18.2. Копирование каталога /home/jane в каталог /work/bkup с помощью команды tar Если в вашей системе есть утилита «А (1.зз), можете запустить процесс записьJar-архива в удаленной системе. Чтобы скопировать каталог в компьютер с именем, скажем, kumquat, введите такую команду: % rsh kumquat rnkdir /work/bkup/jane % tar cf - . | rsh kumquat 'cd /work/bkup/jane ££ tar xBf 1 -' - JP Это значит, что для жесткой ссылки будет создан новый индексный дескриптор и другие скопированные жесткие ссылки будут связаны уже с ним. — Примеч. ред. 290 Часть третья. Работа с файловой системой
19 Создание и чтение архивов 19.01 Переезжаем на новую квартиру Переезд на новую квартиру имеет одну неприятную сторону — хлопоты, связанные с перевозкой вещей. Чем больше таковых вы накопили, тем труднее их упаковать и перевезти. Операционная система UNIX напоминает большой дом, в котором периодически возникает потребность перемещаться с места на место. К счастью, файловая система UNIX позволяет упорядочить персональные файлы пользователя в иерархическом дереве каталогов, аналогичном гораздо большему дереву, охватывающему всю файловую систему. Вы можете подобно белке рассовать всю полезную информацию по маленьким "дуплам". Обычно глубина иерархии дерева персональных каталогов пользователя ограничивается двумя-тремя уровнями, но на практике таковых может существовать сколько угодно. Причем чем дольше используется система, тем сложнее становятся каталоги, файлы и их организация и тем больше скапливается файлов, о которых все позабыли. Эта глава посвящена решению проблем, которые могут возникнуть при перемещении группы файлов, находящихся в одном или нескольких каталогах, из одного места в другое. Вам, возможно, приходилось выполнять резервное копирование ро.ю). Это способ решения проблемы сохранения и восстановления файлов. Если, например, вы перемещаете свою учетную запись и файлы в другую систему, то можете просто попросить системного администратора (если таковой имеется) заархивировать их на ленте или на дискетах, а затем восстановить на новом месте. Многие начинающие пользователи не знают, что при помощи программ создания резервных копий tar (20.00 и cpio (П.ю) (как и при использовании некоторых популярных архивирующих программ, и в частности shar (19.02)) можно создавать личные архивы, которые легко перемещать из одного места в другое. Такая необходимость чаще всего возникает в сетевой среде. Вы можете упаковывать файлы с целью передачи другому пользователю. Файлы могут направляться в Usenet или архивный узел в Internet для распространения среди многих пользователей. Несмотря на принятое нами решение не представлять в этой книге информации по сетям (1.32), упомянутые выше программы настолько полезны даже в локальной системе, что мы решили все же включить данную главу. - TOR 19.02 Shell-архивы: введение Shell-архив является файлом, содержащим один или несколько других файлов. Файлы извлекаются из такого архива с помощью стандартного интерпретатора Bourne shell (44.0З). Shell-архив не предоставляет возможности сохранять и восстанавливать полные иерархии каталогов, как это делают программы cpio 09.09) и tar (19.0S). Но такой архив абсолютно переносим и по этой причине широко используется в Usenet — международной сети, объединяющей многие UNIX-системы. Создание и чтение архивов 291
19.02 Используемый в интерпретаторе Bourne shell оператор « (S.is) означает, что в качестве ввода для команды нужно взять следующие строки, вплоть до указанной. (Такую конструкцию часто называют документ здесь.) Воспользовавшись этим синтаксисом, а также командами cat (25.02) и echo (S.m), можно написать простой shell-архиватор (shar), подобный приведенному ниже. Между прочим, многие системы уже содержат программу shar. Существует несколько ее свободно распространяемых версий, в том числе на компакт-диске. Почти все они являются более сложными по сравнению с приведенной здесь, однако нас это не смущает, так как не мешает продемонстрировать принципы создания подобных программ. #!/bin/sh for file do echo "echo restoring $file" echo "cat > $file « 'XxXxXxXxXx-EOF-XxXxXxXxXx'" cat $file echo "XxXxXxXxXx-EOF-XxXxXxXxXx" done Строка XxXxXxXxXx-EOF-XxXxXxXxXx набирается произвольно. Она не может появиться в потоке ввода, а должна использоваться интерпретатором shell для распознавания конца документа. Если мы предоставим программе shar список имен файлов, то она направит все эти файлы в стандартный вывод, вставляя между ними упомянутую произвольную строку и команды, позволяющие разделить их опять. Чтобы создать архив, достаточно переадресовать этот выходной поток в файл. Так, команда $ shar filel file2 > archive.shar создаст файл archive.shar, содержащий следующие данные: echo restoring filel '...' 4S.26 cat > filel « ' XxXxXxXxXx-EOF-XxXxXxXxXx' Текст файла filel XxXxXxXxXx-EOF-XxXxXxXxXx echo restoring file2 cat > file2 « 'XxXxXxXxXx-EOF-XxXxXxXxXx' Текст файла file2 XxXxXxXxXx-EOF-XxXxXxXxXx При обработке этого архива интерпретатором sh содержащиеся в архиве команды будут выполнены. Каждый документ (строки между каждой командной строкой cat и следующей строкой XxXxXxXxXx-EOF-XxXxXxXxXx) выводится в файл: $ sh archive.shar restoring filel restoring file2 $ Is archive.shar filel file2 Программа unshar (19.03) делает то же самое. for 44.16 « S.IS m Часть третья. Работа с файловой системой
19:04 Не стоит запускать на выполнение файлы shell-архивов, полученные от незнакомых вам людей. Посредине безобидного, на первый взгляд, архива злоумышленник может без труда вставить "троянского коня" в виде такой команды, как гт *, что принесет вам множество неприятностей. Избежать этого вам поможет программа тоге (25.оз). Воспользуйтесь командой поиска (в программе тоге таковой является команда /), чтобы найти строку окончания файла (например, XxXxXxXxXx-EOF-XxXxXxXxXx). Внимательно просмотрите команды, находящиеся между этой строкой и строкой cat, которая начинает следующий файл. Если файлы в shell-архиве сами являются программами, то их перед запуском также следует проверить. - TOR 19.03 Программа unshar: разархивирование shell-архивов В параграфе 19.02 показано, как с помощью интерпретатора sh извлечь файлы из shell-архива путем выполнения последнего в качестве сценария. Однако этот способ имеет несколько недостатков. Один из них связан с тем, что jAo/^файлы часто посылаются по электронной почте и поэтому содержат специальный заголовок перед командами интерпретатора shell. Конечно, этот заголовок можно удалить вручную, но зачем вам лишние заботы? Программа unshar, которая наряду с профаммой shar записана на компакт-диске, имеет возможность игнорировать "мусор" в начале архива и проверять последний на предмет возникновения проблем. - TOR 19.04 Простая версия команды unshar Предлагаемый ниже небольшой сценарий является прекрасным примером того, как много способна сделать простая профамма unshar. Она пропускает заголовок электронной почты и комментарий, которые присутствуют в начале некоторых shell-архивов (I9.02), а затем передает этот архив интерпретатору shell. Сценарий можно использовать при чтении сообщений с помощью большинства излмеющихся почтовых профамм UNIX о.ззу. & save j unshar v или предоставив ему имя архивного файла в командной строке: % unshar файл.shar Вот эта версия сценария: #! /bin/sh # Игнорирование строк перед'первым символом комментария ("#"), # стоящим в первом столбце: sed -n '/"#/,$р' $1 I sh Сценарий читает данные со своего стандартного ввода или из отдельного файла. Он пропускает все сфоки, пока не всфетит комментарий (#), которым начинается большинство shell-архивов. Остальные сфоки передаются по каналу в интерпретатор shell. Сценарий не может читать несколько файлов архива. Для этого нужно было бы добавить еще пару строк консфукции цикла, что сделало бы сценарий слишком длинным! :-) В последние годы появились намного более сложные версии профаммы unshar оя.оз). Они защищают от "фоянских коней", спрятанных в архивах, и позволяют системам, не содержащим интерпретатора Bourne shell (например, DOS), распаковывать shell-архивы. Однако и простейший сценарий прекрасно справляется со своей работой. [Кроме того, он еще раз демонстрирует, насколько важно для кандидата в фамотные пользователи UNIX иметь хотя бы элементарное представление о редакторе sed (34.24). Утилита unshar — чрезвычайно удобная вещь! — TOR] - JP Создание и чтение архивов 293
19.05 19.05 Использование программы tar для создания и распаковки архивов Многие пользователи UNIX считают, что программа tar (20.01) служит только для создания ленточных архивов. Но это лишь одна из областей ее применения, как, впрочем, и в случае большинства утилит UNIX. Утилиту tar, в частности, можно использовать для копирования дерева каталогов os.it). Наиболее часто программа tar применяется для создания архивных файлов, которые можно передавать в другие системы. Мы уже рассматривали утилиту для создания shell-архивов от), но есть много задач, которые с ее помощью выполнить невозможно. Программа tar очень удобна для пересылки двоичных данных. Мне встречались утилиты shar, которые также работают с двоичными данными, однако таковых немного и, кроме того, мне не нравится, как они это делают. При использовании программы tar вы можете несколько каталогов упаковать в один архив, переслать каталоги, содержащие ссылки, сохранить принадлежность и права доступа к файлу и т.д. При необходимости создать far-архив используйте опции с и/(последняя позволяет сохранить вывод программы tar в файле): % cd /home/src/fsf % tar cf emacs.tar emacs Команда помещает все содержимое каталога emacs в файл emacs.tar (называемый far-файлом). Затем этот файл с помощью утилит FTP, UUCP о.зз) и прочих средств можно передать другим пользователям. Независимо от способа создания, архивы обычно имеют довольно большой объем, поэтому их часто сжимают с помощью команд, аналогичных приведенной ниже: % gzip emacs.tar При этом создается файл emacs.tar.gz, который, как правило, намного меньше исходного far-архива. Если вы для передачи файла решили использовать утилиту UUCP или FTP, можете делать это смело. Обе утилиты могут работать с двоичными данными. Однако часто возникает необходимость переслать архив по электронной почте (1.зз), а многие почтовые программы работают только с ASCH-Данными (St-оз). В этом случае нужно создать ASCII-версию архива. Чтобы это сделать, используйте команду uuencode (S3.oi). Команда создания закодированного архива имеет такой вид: % uuencode emacs.tar.gz emacs.tar.gz > emacs.tax.gz.uu После этого файл emacs.tar.gz.uu можно вставить в сообщение электронной почты и послать кому нужно. Правда, ASCII-кодирование вносит избыточность: объем закодированного файла возрастает примерно на 33%.* Если пойти дальше, то можно объединить оба описанных действия в одной команде. Если вы укажете только имена архивируемых файлов, то создаваемый архив будет направлен на стандартный вывод. Далее он попадет в канал: % tar cf - emacs j gzip | uuencode emacs.tar.gz > emacs.tax.gz.uu Что нужно сделать после получения файла, сжатого и закодированного с помощью утилит gyp и uuencode? To же самое, что и перед передачей, но в обратном порядке. Полученное почтовое сообщение (без учета различных заголовочных строк) может выглядеть примерно так: begin 644 emacs.tar.gz M+DQ0"D%L;,,!O9b!T":&5S92!P<F]B;&5M<R!C86X@8F4@<V]L=F5D(&)Y(")L Если это так, то зачем, спросите вы, сжимать файл с помощью программы gzip"! Нельзя ли вообще забыть о ней и программе uuencode'. К сожалению, это невозможно. Вспомните, что rar-архивы являются двоичными, даже если все файлы в архиве относятся к текстовым. Передаче по электронной почте в любом случае должен предшествовать этап обработки файла с помощью программы uuencode, после выполнения которой исходный файл увеличивается на треть. Уменьшить объем файла можно с помощью программы gzip. 294 Часть третья. Работа с файпоаой системой
19.06 M:6YK<RPB(&@;65C:&%N:7-M('=H:6-H"F%L/S]W<R!A(S9I;S4@=s\@;SV M92!T=V\@;W(@;6]R92!N86UE<RX@(%5.25@@<')0=FED97,@='=0(S1I9F9E M<F5N= IK: 6YEKR! 09B! L: 6YK<SH* + DQS ($ (* + DQI"EQFODAA<FO@; &EN:W-< Итак, вы сохраняете сообщение вместе с заголовками в файле, названном, скажем, mailstuff. Как из него можно получить исходные файлы? Рекомендуем воспользоваться для этой цели следующей цепочкой команд: % uudecode mailstaff % gunzip emacs.tar.gz % tar xf emacs.tar Команда uudecode создает файл emacs.tar.gz. Затем команда gunzip воссоздает исходный tar-аршв, а команда tar xf извлекает из него отдельные файлы. В параграфе 19.07 описан более эффективный способ извлечения файлов из архивов, а также приведено объяснение команды tar о, столь необходимой многим пользователям System V. Между прочим, программа tar является такой гибкой именно благодаря файловой ориентации UNIX: здесь все, в том числе и ленточный накопитель, рассматривается как файл. Поэтому программа tar создает файл определенного вида и посылает его во "внешний мир" — обычно он помещается на ленту, но при желании вы можете хранить такой файл и в каком-либо другом месте. В большинстве операционных систем утилита для работы с ленточным накопителем может "общаться" только с ним. - ML 19.06 GNU-версия программы tar ИGNU-вepcия программы tar имеет очень много функций. Некоторые считают, что даже слишком много, однако я не согласен с таким мнением. Для этой версии характерны функции, которые я со временем хотел бы видеть в более "стандартных" версиях. Наиболее любимые for мною функции перечислены в настоящем параграфе. Полный их перечень можно найти в соответствующей документации, представленной на компакт-диске. • В параграфе 19.05 рассказано, как можно сжать созданный архивный файл. Если вы воспользуетесь программой GNU tar, то сделать это будет еще проще, поскольку программа tar может сама выполнять сжатие. Просто при записи и чтении архивов необходимо применить опцию Z. Например, команда % tar cvZf /dev/rstO создает сжатый /or-архив на ленточном накопителе с именем /dev/rstO. Попробуйте это сделать, если хотите поместить на ленте больше данных, чем рассчитывал изготовитель. • Мне не раз приходилось совершать классическую ошибку, заключающуюся в архивировании файлов с их полными путевыми именами (2<uo). GNU tar предупреждает возникновение подобных ошибок. Она всегда сохраняет полные путевые имена как относительные, если только вы не добавляете опцию —absolute-names. • Часто бывает нужным создать резервную ленточную копию последних рабочих файлов большого проекта (но не всех из числа многих тысяч файлов, входящих в проект). Это можно сделать с помощью двух команд: find -mtime, позволяющей создать список, и команды tar -I. Однако результат зачастую получается неудовлетворительным. GNU tar предоставляет выход . из такого положения: ее опция —after-date дает возможность определить каталоги, какие следует просмотреть, и файлы, которые нужно поместить в архив. • После извлечения из архива файлы можно записывать в каталог, в котором уже есть другие файлы. Опция —keep-old-files сообщает программе GNU tar, что существующие файлы переписывать не следует. Одно предупреждение относительно программы GNU tar: она создает tar-архивы в формате ANSI. Извлечение файлов из такого архива с помощью старой версии программы tar может вызвать предупреждающее сообщение типа tar: unexpected EOF (непредвиденный символ конца файла). Но для создания архивов в старом формате программа GNU tar, конечно же, имеет опцию —old-archive. - JP, TOR Создание и чтение архивов 295
19.07 19.07 Извлечение файлов из сжатых архивов В параграфе 19.05 мы рассказали, как можно создать, а затем развернуть сжатый /аг-архив- сначала нужно запустить программу gunzip (24.07), а затем — программу tar. Эти действия можно объединить, воспользовавшись программой gzcat и передав ее вывод по каналу программе tar. % gzcat archive.tar.gz | tar xf - яутвяпа_ямеяа Команда gzcat распаковывает файл (формата gzip и более старых форматов — compress и pack), посылая результат на стандартный вывод. Если после опции / в качестве имени файла указывается символ ^ (13.13), программа tar читает данные со стандартного ввода. При этом нет необходимости создавать несжатые файлы большого размера; архив можно постоянно хранить в сжатом виде. Чтобы извлечь из архива только некоторые файлы, укажите в командной строке их путевые_имена точно в таком виде, в котором они были сохранены в архиве. В противном случае программа tar извлечет все файлы. (При необходимости получить перечень точных путевых имен используйте команду tar tf -.) Если вам часто приходится извлекать файлы, создайте для выполнения соответствующей операции псевдоним: alias ztar "gzcat \!Л I tar xf -" Этот же прием можно использовать для создания архива, сжимаемого при помощи утилиты gzip- % tar cf - спясок_файлов | gzip > archive.tax.gz W/ Вместо опции cf можно использовать опцию cvf — в таком случае программа tar будет *"\ч выводить имя каждого обрабатываемого файла. [19.08] Примечание: Файлы, извлеченные из архива, созданного не вами, могут и принадлежать не вам. Объясняется это следующим образом. Во многих не-BSD системах при извлечении файла программой tar идентификатор владельца (TJID) (за.оз) не изменяется. Если это не ваш идентификатор, то программа tar извлечет каталоги и файлы, с которыми вы не сможете работать. В случае возникновения такой проблемы вы можете добавить в свою программу tar опцию о (например, tar xof), что сделает вас владельцем извлекаемых файлов. - ML, ПК, JP 19.08 Проблемы с диагностическими сообщениями программы tar Я слышал об одной версии программы tar, которая при использовании опции v записывает словесную информацию в стандартный поток вывода, а не в стандартный поток ошибок. Если ваша программа tar делает так же, постарайтесь не использовать опцию v при записи вывода в канал. Например, следующая команда может привести к созданию архива с некорректной структурой, если программа tar содержит эту ошибку: % tar cvf - *.txt | gzip > archive.tar.gz Имена файлов могут появиться в стандартном выводе вместе с их содержимым. В результате получится сжатый архив, который невозможно будет раскрыть. (Если вы попытаетесь это сделать, то, скорее всего, получите от программы tar сообщение об ошибке в контрольной сумме или что-нибудь в этом роде.) 296 Часть третья. Работа с файловой системой
19.09 Проверить возможность возникновения такой проблемы можно с помощью команды % tar cvf - файл > /dev/null утилита tar без ошибки в опции v файл 23 blocks, 44567 characters Здесь стандартный вывод перенаправляется в устройство /dev/null оз.и). Если вы не видите никаких сообщений, значит, ваша программа tar содержит ошибку. - JP 19.09 Архиватор для System V: программа cpio Было время, когда пользователи UNIX спорили о том^ какая из программ создания архивов и резервных копий лучше: tar (20.01, nos) в BSD или cpio в System V. Сейчас подобные вопросы не возникают. В Сети о.зз) программу cpio никто не применяет — повсеместно используется программа tar. А поскольку ее бесплатные версии, в том числе и GNU tar (П.оь), вполне доступны, то читать чей-либо ср/о-архив нет необходимости. И все же, работая на старой машине с System V, вы можете использовать программу cpio. Мы не станем рассказывать о ней подробно, но некоторые основные сведения все же приведем. • Для создания архива используется опция -о и осуществляется переадресация вывода либо на ленточное устройство, либо в архивный файл. Список файлов, подлежащих архивированию, часто определяется с помощью команды .find 07.00, но может быть создан и другими способами — программа cpio читает список файлов со своего стандартного ввода. Например: % find . -name "*.old" -print | cpio -ocBv > /dev/rst8 или % find . -print I cpio -ocBv > mydir.cpio • Для чтения из архива используется опция -/ и выполняется переадресация ввода из файла или ленточного накопителя, содержащего архив. Часто возникает необходимость в использовании опции -d, которая заставляет программу cpio создавать при копировании файлов нужные каталоги. Вы можете восстановить все файлы из архива или задать шаблон имен файлов (с метасимволами, защищенными с помощью кавычек или обратной косой черты), чтобы выбрать только некоторые из них. Например, следующая команда воссоздает из ленточного накопителя все исходные файлы, написанные на С: % cpio -icdv "*.c" < /dav/rst8 Если нужно, создаются подкаталоги (-d), и программа cpio (опция -v) выводит сообщение о каждом успешно прочитанном файле. • Для копирования архива в другой каталог используется опция -р и указывается имя каталога назначения. (Это одна из самых интересных функций программы cpio.) Чтобы скопировать, скажем, содержимое текущего каталога (включая все подкаталоги) в другой каталог, можно воспользоваться такой командой: % find . -depth -print j cpio -pd newdir • Существуют опции и для выполнения многих других операций, таких как изменение времени доступа, принадлежности файла или размера блока на ленте. За более подробной информацией по этой теме обращайтесь к интерактивному руководству. Обратите внимание, что опции "смешаны" в одной строке, а не записаны раздельно. - TOR 'Создание и чтение архивов 297
20 Резервное копирование файлов 20.01 Краткое резюме по утилите tar Многие пользователи UNIX, говоря об архивах — либо на ленте, либо в архивном файле, вспоминают утилиту tar. Но существуют и другие средства создания архивов и работы с ленточными накопителями, в том числе такие профаммы, как cpio (пщ, shar (19.02) и dd (20.O6). В настояшем параграфе обобщены сведения, приведенные в этой и других посвяшенных программе tar главах. • Хотя утилита tar разрабатывалась как ленточный архиватор, для нее типичным является создание архивных файлов на диске (i9.os). И поскольку программа tar "набивает" свои архивы символами NUL (si.oj), объем файла Го^архива на диске часто бывает большим, чем суммарный объем содержащихся в нем файлов. Для сжатого архивного файла может понадобиться распаковка архива (19.07). GNU tar (19m) имеет возможность сжимать архивы при их сохранении. При создании архивов на диске будьте внимательны с опцией v профаммы tar, поскольку велика вероятность появления поврежденного архива, содержащего не только файлы (19.щ. Сжатый й^архив часто занимает меньше дискового пространства (24.08), чем отдельные сжатые файлы. • Поскольку утилита tar сохраняет большую часть информации из индексного дескриптора (1.22) файла, она может создавать более полную копию (is.n) файла или каталога, чем другие утилиты копирования, например ср. • Да, мы все же приводим парафафы, посвященные ленточным архивам. В параграфе 20.02, написанном Брюсом Барнеттом, имеется достаточно информации для создания собственного архива, хотя могут понадобиться и сведения, содержащиеся в параграфе 20.03. Создав архив, вы, вероятно, захотите ознакомиться с его содержимым, по крайней мере для того, чтобы убедиться в корректности содержащихся там данных. В парафафе 20.04 рассказано, как это сделать. В случае отсутствия в вашем компьютере ленточного накопителя прочтите в парафафе 20.05 о возможности использования накопителя на другом компьютере. Если этой информации окажется недостаточно, познакомьтесь в парафафе 20.06 с более детальной информацией, добытой другим нашим старым экспертом по UNIX и ленточным архивам — Крисом Тореком. • ПрОфамма tar копирует дерево каталогов рекурсивно, сверху донизу. А что если вы не хотите архивировать все подряд? В таком случае путем комбинирования команд Is -It и find (20.07) можно сохранить только некоторые из файлов. Отдельные версии профаммы tar имеют опции, позволяющие включать или исключать определенные файлы и каталоги очм, 2о.щ. - JP 298 Часть третья. Работа с файловой системой
20.03 20.02 Создавайте резервные копии Как человек, которому приходилось быть и рядовым пользователем, и системным администратором, я глубоко убежден, что каждый пользователь должен осознавать важность резервного копирования. Примечание: При наличии данных, представляющих определенную ценность, создавать резервные копии просто необходимо. Всегда существует вероятность каких-либо недосмотров и даже аварий. Ленты могут быть повреждены, утеряны или неправильно маркированы. Предположим, вас обслуживает первоклассный системный администратор. Но даже самый лучший администратор может восстановить утерянные данные лишь в 99% случаев. Можете ли вы себе позволить повторно делать месячный объем работы хотя бы в 1% случаев? Думаю, что нет. Жизнь заставляет опытного пользователя быть пессимистом. Обычно такой урок дается тяжело. Ваши потери могут равняться нескольким часам или даже дням работы. Иногда теряются месяцы... Вот несколько типичных ситуаций. • Пользователь весь день трудился над файлом. В конце дня файл случайно удаляется. Системный администратор не в состоянии восстановить файл. День работы потерян. • Программист пытается почистить каталог проекта. Вместо того чтобы набрать rm *. о, он набирает rm * .о, и весь каталог теряется. • Пользователь случайно удаляет файл и лишь спустя несколько дней просит системного администратора восстановить его. Но система инкрементного резервного копирования уже повторно использовала ленту, на которой был записан нужный файл. • Большой проект архивируется на магнитную ленту и удаляется с диска. Спустя год возникает потребность в некоторой информации. В начале ленты есть плохой блок. Системный администратор должен найти способ восстановить данные с поврежденной ленты. Но нередко попытка сделать это оказывается безуспешной. Информация безвозвратно теряется, и на ее восстановление зачастую уходят месяцы работы. • Кто-то проникает в компьютер и получает доступ к закрытой информации. • В компьютерной комнате возникает пожар. Диски и все резервные ленты пропадают. Уфф! Самому стало страшно. Подождите минуточку, пока я загружу ленту... Фу-у... Теперь полегчало. Как говорится, лучше быть пессимистом... Создать резервную копию Просто. Возьмите чистую ленту и поставьте на ней метку. Научитесь заправлять ее в накопитель. Потом введите следующее: % cd % tar с . Извлеките ленту, защитите ее от записи (сдвиньте язычок, поверните рычажок или вытащите кольцо). Вот и все. [Не совсем! Брюс установил, что можно обеспечить еще большую степень безопасности, используя системы управления версиями файлов SCCS (20.12) или RCS (20.Н). Предназначены оии в первую очередь для хранения версий файлов, которые вы часто обновляете. — ТОЩ - ВВ 20.03 Создание резервных копий с помощью локального ленточного накопителя Как уже было сказано, создать резервную копию просто: % cd % tar с . Резервное копирование файпов 299
20.03 Команда cd обеспечивает переход в начальный каталог. Этим же способом можно создать резервную копию любого каталога. Команда tar (20.01), имя которой образовано от tape archive (ленточный архив), копирует текущий каталог, заданный именем ., на ленточный накопитель, задаваемый по умолчанию. Аргумент с определяет режим создания (create) архива программы tar. Но может появиться сообщение об ошибке, например о том, что устройство rmt8 выключено. Не волнуйтесь. Я просто немного преувеличил, говоря, что пользоваться утилитой tar легко. Устройством, используемым системой SunOS по умолчанию для работы с ленточным накопителем, является /dev/rml8 (у вас оно может быть другим). Существует несколько типов ленточных накопителей, но не ко всем из них можно обращаться посредством указанного имени. Некоторые системные администраторы могут привязать это имя к используемому устройству, что облегчает работу с программой tar. Но если ничего не работает, то для команды tar нужно задать дополнительные аргументы. Синтаксис команцы tar Большинство команд UNIX требуют выполнения определенных правил при задании аргументов. Команда tar не соответствует подобным соглашениям, поэтому, чтобы использовать ее корректно, необходимо быть очень внимательным. Если следовать стандартным правилам, то представленная ниже командная строка должна обеспечить копирование текущего каталога на ]/2-дюймовый ленточный картридж в режиме с выводом сообщений и размером блока 20: % tapedump -с -V -Ь 20 -f /dev/rmtS . Неправильно! Но на самом деле, все флаги должны быть в первом аргументе, а дальше должны следовать параметры — в том порядке, в котором приведены флаги: % tar cvbf 20 /dev/rmt8 . Эту же команду можно задать другим способом — путем изменения порядка следования букв первого аргумента: tar cvfb /dev/tmt8 20 . (О порядке следования аргументов можно судить по рисункам, содержащимся в параграфе 20.11.) Единственный ключевой символ, имеющий фиксированное положение, — это первый символ, который нужно задавать при чтении и записи архива. Наиболее часто используемые ключевые символы и выполняемые ими функции перечислены в-табл. 20.1. Таблица 20.1. Обычные ключевые символы команды tar Символ Функция с X t V Создание архива Извлечение файлов из архива Вывод списка содержимого архива Вывод сообщений Некоторые версии команды tar требуют наличия перед флагом символа дефиса (-). Имя ленточного накопитепя Одна из трудностей, с которой приходится сталкиваться в случае применения команды tar, заключается в выяснении того, какое имя файла следует использовать для работы с тем или иным устройством. Лучше всего об этом спросить у системного администратора. Но если у вас нет такой возможности, попытайтесь выяснить это самостоятельно, воспользовавшись некоторыми идеями, справедливыми для компьютеров Sun. Если вы пользуетесь накопителем на ленте шириной 1/2 дюйма, попробуйте применить команду % tax cf /dev/rmt8 . 300 Часть третья. Работа с файловой системой
20.03 При использовании накопителя на ленте шириной 1/4 дюйма [или любого накопителя, подключенного через интерфейс SCSI — JP], введите команду % tar cf /dev/ret8 . Если она не сработает, попробуйте вместо 8 набрать 0. Можно также вывести список файлов устройств в каталоге /dev и найти то, которое использовалось последним: -\AH.02 % Is -lut /dev/r[ms]t* И 15.02 Некоторые UNIX-системы применяют другие правила наименования накопителей на магнитных лентах. В частности, в конце их имени может присутствовать буква h (режим записи с высокой плотностью). Если есть сомнения, проверьте старший и младший номера устройства (17.22) (с помощью команды Is -/) и прочтите соответствующую страницу руководства, которую можно найти путем просмотра возможных записей с помощью команд man -к или apropos (so.cny. % man -k mt % man -k tape Еще об именах ленточных накопителей Имена ленточных накопителей обычно начинаются с буквы г, указывающей на то, что последние являются неструктурированными (raw) устройствами хранения, не поддерживающими файловую систему. Если первыми буквами являются пг, то это предполагает работу без перемотки ленты (no-rewind). Обычно лента автоматически перематывается после окончания работы. Повторный вызов команды tar приведет к тому, что первоначальная копия будет перезаписана.* Если копии небольшие и, следовательно, возможны значительные затраты ленты, используйте имя с пг — в таком случае вы сможете разместить несколько копий на одной ленте. Если, например, вы хотите скопировать три отдельных каталога на ленточный картридж формата 1/4 дюйма, наберите команды: % cd dirl % tar cf /dev/nxete . % cd dir2 % tar cf /dev/nrst8 . % cd dirt % tar cf /dev/ret8 . Обратите внимание на тот факт, что в третьей копии не указывается имя устройства, задающее режим без перемотки, поэтому по окончании операции копирования лента будет перемотана. Вы можете просмотреть ленту без извлечения файлов, получив список ее содержимого. Для этого вместо буквы с необходимо задать t или tv. Добавление флага v обеспечивает получение более подробного списка. Если вы хотите исследовать третий файл дампа, можете либо дважды выполнить команду tar в режиме без перемотки, либо переместиться на один или несколько файлов вперед, используя команду mt с параметром (в данном случае он равен 2). Не забудьте, что нужно использовать имя, обеспечивающее режим без перемотки: % mt -f /dev/nxet8 fsf 2 В некоторых версиях команды tar, если определена переменная среды ТАРЕ setenv TAPE /dev/rst8 то в командах mt и tar имя устройства задавать не нужно. - ВВ Это, скорее всего, не то, что вам нужна Попробуйте использовать для каждой копии новую ленту. Резервное копирование файпов 301
20.04 20.04 Восстановление файлов с ленты посредством команды tar При создании архива имеется несколько способов задания каталога. Если каталог принадлежит текущему каталогу, можно ввести команду % tar с project Задать каталог можно и так: % tar с ./project Если нужно архивировать текущий каталог, введите команду % tar с . Текущий каталог можно архивировать и следующим образом: % tar с * В последнем случае интерпретатор shell меняет * (звездочку) на имена файлов текущего каталога. Однако он пропускает файлы с именами, начинающимися с . (точки), поэтому предыдущий способ считается более предпочтительным. Это вызывает проблемы при восстановлении каталога из tar-архива. Вы можете не знать, создан архив с использованием обозначения . или же было указано имя каталога. Я всегда проверяю имена файлов перед их восстановлением из архива: % tar t Если список файлов архива не содержит полных путевых имен, необходимо создать новый каталог, перейти в него и, уже будучи там, извлечь файлы. В том случае, если имена файлов предваряются именем каталога, я распаковываю файлы в текущем каталоге. Восстановление отцельных файлов Если нужно восстановить один файл, узнайте его путевое имя с помощью команды tar t. Имя файла нужно указывать точно, поскольку файл и ./файл — разные имена. Вы можете объединить два действия в одной команде [иногда она выполняется очень медленно. — JP\: % tar xvf /dev/rsrO tar tf /dev/rstO I grep файл' Если вы используете команду tar с целью восстановления каталога, в команде grep нужно обязательно указывать имя файла. Если этого не делать, то ни один файл не будет восстановлен. Еще одна проблема связана с восстановлением каталога, имя которого начинается с косой черты (/). Поскольку команда tar восстанавливает файлы с путевыми именами, указанными в архиве, то изменить место, куда будет записан такой файл, невозможно. Существует опасность того, что вы либо перепишете уже существующий файл, либо не сможете восстановить файл из-за отсутствия соответствующего права доступа. Вы можете попросить системного администратора переименовать каталог и временно создать символическую ссылку на каталог, в котором можно восстанавливать файлы. Существуют и другие решения, в том числе редактирование far-архива и создание новой структуры каталогов с помощью программы на языке С, выполняющей системный вызов chroot(2). Еще одно решение состоит в использовании версии команды tar от организации Free Software Foundation (SJ.oi). В таком случае вы сможете изменить систему путевых имен, начинающихся с косой черты (/). Эта бесплатно распространяемая версия команды tar (записана на компакт-диске) называется также GNU tar (В.об). Она позволяет создавать многотомные и дополняемые архивы, имеет ряд других преимуществ. Но самое лучшее решение — никогда не создавать архив каталога, указывая его полное путевое имя (начинается с /) или тильду (~) (i4.it). 302 Часть третья. Работа с файловой системой
20.06 Восстановление файлов с удаленных машин При восстановлении каталога из удаленного сервера используется такая команда: rsh 1.33 % rsh -n host dd if=/dav/rstO bs=20b | tar xvBfb -20 файла Блоки фиксированного размера трудно читать по сети. Вот почему в команде tar используется флаг В, "заставляющий" ее читать данные из канала до тех пор, пока не заполнится блок. [Некоторые версии команды tar, например GNU-верспя О9.оь), управляют удаленными ленточными накопителями автоматически. — ЛК] - ВВ 20.05 Использование команды tar для работы с удаленными ленточными накопителями Если к вашему компьютеру ленточный накопитель не подключен, то процесс создания резервных копий с помощью команды tar (20.01) несколько усложняется. При наличии учетной записи на машине с ленточным накопителем и при условии, что нужный каталог смонтирован на удаленной машине с использованием NFS (из), можно просто зарегистрироваться на удаленной машине при помощи утилиты rlogin f/.-Ц) и применить команду tar для создания резервной копии своего каталога. Если к каталогу нет доступа с помощью NFS или же если у вас возникли проблемы с правами доступа к собственным файлам, для разрешения проблемы можете использовать команды tar, rsji (из) и dd (35.06). Их синтаксис несколько необычный, и если вы его забыли, воспользуйтесь командой man (so.oi). Командная строка, задающая копирование текущего каталога на ленту удаленной машины с именем zephyrus, выглядит следующим образом: % tax cvfb - 20 . I rsh zephyrus dd of=/dev/rmt0 obs=20b Здесь выходной файл команды tar имеет имя ^ С3'3), которое интерпретируется последней как стандартный ввод при чтении из архива или как стандартный вывод при создании архива. Команда dd копирует данные из стандартного ввода в устройство /dev/rmtO. О том, как она функционирует, вы можете узнать из параграфа 20.06. В этом примере предполагается, что вы можете использовать утилиту rsh без ввода пароля. Если в случае применения утилиты rlogin для доступа к удаленной машине вы получаете приглашение Password:, можете добавить имя своей машины в файл .rhosts о.зз) на удаленной машине. Пароль в таком случае запрашиваться не будет. - ВВ 20.06 Запись на ленточный накопитель на удаленной машине [Брюс Барнетт начал эту тему в параграфе 20.05. — JP] В заметке в группе новостей <5932@tahoe.unr.edu> malc@equinox.unr.edu Малкольм Карлок (Malcolm Carlock) спрашивает, как заставить команду tar записывать данные на удаленный ленточный накопитель с помощью команд rsh о.зз) и Л/ (35М). Вот ответ: % tar cf - . I rsh foo dd of=/dav/устройство obs-20b Имейте в виду, что большинство реализаций команды dd работают в приведенной команде очень медленно. Что здесь происходит? Чтобы понять это, нужно разобраться в следующем. • Одной из характеристик ленточных накопителей является размер блока. Но знать его не всегда нужно — основная масса накопителей, подключаемых через интерфейс SCST, имеет фиксированный размер блока, поэтому в большинстве случаев такую информацию можно проигнорировать. Однако 9-дорожечные накопители помещают данные в записи, разделенные метками, поэтому впоследствии можно будет считывать только целые записи. Резервное копирование файлов 303
20.06 • Чтобы приспособиться к этому, большая часть ленточных драйверов UNIX транслирует каждый системный вызов readQ или writeQ в процесс передачи одной записи. Размей записи равен количеству байтов, переданных вызову writeQ. (Могут существовать дополнительные ограничения типа "размер должен представляться четным числом" или "размер не может превышать 32768 байтов". Обратите внимание на тот факт, что в ленточных накопителях с фазовым кодированием (1600 битов/дюйм) блоки должны быть длиной не более 10240 байтов, а в устройствах с форматом записи GCR (6250 битов/дюйм) — длиной не более 32768 байтов. Это уменьшает вероятность возникновения некорректируемой ошибки.) Каждый вызов readQ должен запрашивать по крайней мере одну запись (некоторые драйверы работают неправильно — без предупреждения отбрасывают остаток записи, выходящий за размер, указанный в вызове readQ) и всегда возвращать информацию о реальном количестве байтов в записи. • Сетевые соединения, как правило, представляют собой потоки байтов: два сервера (как и в предыдущем примере, одна машина запускает команду tar, а другая имеет ленточный накопитель) будут обмениваться данными, но на уровне сетевого протокола отбросят все разделители записей. Если разделители записей необходимо сохранить, то это будет сделано на другом уровне, более высоком, чем уровень самого сетевого протокола. (Не все сетевые протоколы являются ориентированными на потоки байтов, а многие даже не управляют потоками данных и не корректируют ошибок. Такие протоколы Internet, как RDP и XNS SPP, являются примерами надежных протоколов, ориентированных на передачу записей. Правда, многие из них накладывают требование, согласно которому размер.блоков должен быть относительно небольшим.) • Утилита rsh просто организует передачу потоков на уровне сетевого протокола, не выполняя никакой работы по сохранению границ пакетов. • Команда dd работает несколько загадочным образом: dd if=x of=y Это то же самое, что и dd if^x of=y ibs=512 obs=512 Данная команда означает следующее: открыть файлы хну, затем, циклически выполняя вызов read(fd_x), брать получаемые данные и копировать их в выходной буфер файла у и каждый раз, когда в буфер поступает 512 байтов, выполнять единственный вызов write(fd_y) для записи данных в файл. С другой стороны, команда dd if=x of=y bs=512 означает совсем иное: открыть файлы х и у, а затем циклически выполнять вызов read(fd_x), считывая 512 байтов входных данных, и осуществлять вызов write(fd_y). Все это вместе означает, что команда % tar cf - . | rah otherhost dd of=dev/устройство будет записывать блоки размером по 512 байтов (не то, что вам нужно), а команда % tar с£ - . | rsh otherhost dd of=dev/устройство Ьа=20Ь "поступит" еще хуже: она возьмет данные из потока stdin, который, являясь TCP-соединением, будет абсолютно неструктурированным, зависящим от используемых в процессе передачи сетевых параметров и конкретной реализации протокола TCP, и создаст записи практически случайного размера. При чисто локальных соединениях (по Ethernet) в типичных реализациях будут создаваться блоки размером 1024 байта (блок-фактор команды tar равен 2). Если значение блок-фактора, равное 2, приемлемо и если команда cat требует наличия блока размером 1024 байта (оба эти условия в некоторых случаях выполняются), то можно использовать команду % tar cf - . | rah otherhost "cat > dev/'устройство" 304 Часть третья. Работа с файловой системой
20.08 но это зависит от недокументированных возможностей команды cat. В любом случае на 9-дорожечных накопителях каждая разделительная метка занимает примерно 0,7 дюйма ценного ленточного пространства, а при использовании блоков размером 1024 байта меток в 10 раз больше, чем при использовании блоков размером 10240 байтов. Следовательно, в расчете на одну запись это приводит к потере 10 Кб (9x1600x0,7) ленточного пространства при плотности записи 1600 bpi. В случае применения блоков размером 32768 байтов количество меток увеличивается уже в 32 раза, что приводит к потере 136 Кб (31x6250x0,7) ленточного пространства при плотности записи 6250 bpi. Я говорю "примерно 0,7 дюйма" потому, что действительный размер меток может быть разным. В частности, некоторые "потоковые" накопители, или стримеры (они называются потоковыми неоправданно, так как их контроллеры часто не в состоянии справиться с требуемой скоростью передачи данных, даже при использовании прямого доступа к памяти), имеют размер метки порядка 0,9 дюйма. Из-за наличия разделительных меток нужно стараться использовать блоки настолько больших размеров, насколько позволяет система коррекции ошибок. Следует, однако, отметить, что некоторые старые устройства (их можно встретить в отдельных системах AT&T ЗВ) накладывают необычное ограничение на размер ленточного блока — 5 Кб. — СТ, из телеконференции сотр.unix.questions в Usenet, 3 апреля 1991 г. 20.07 Создание файла с временными метками для выборочного резервного копирования При инсталляции новой версии UNIX всегда было сложно убедиться в том, что сохранены все изменения, сделанные в предыдущей реализации UNIX. Раньше я во всех каталогах выполнял команду Is -It (16.02), а затем проверял даты модификации. Файлам, которые были изменены, очевидно, должны соответствовать более новые даты, чем исходным программам [если только измененные файлы не поступили из /ar-архива с сохраненными исходными датами! — JP]. Но даже при таком условии поиск каждого изменения — процесс довольно трудоемкий, поскольку сопровождается исследованием десятков каталогов. Лучшим решением на первом этапе обновления является создание специального файла. Обычно я называю его FirstFile. Команда find имеет опцию -newer (П.щ, которая проверяет каждый файл и сравнивает его дату модификации с соответствующими датами некоторого файла. Если потом нужно вывести список всех файлов каталога /usr, которые должны быть сохранены при обновлении операционной системы, воспользуйтесь командой % find /usr -newr /usr/FirstFile -print Кроме того, этот файл можно использовать для создания с помощью команды tar (i9.os) или cpio (19.09) архива, файлы из которого можно восстанавливать после обновления системы. - ВВ 20.08 Сообщение команде tar о том, какие файлы необходимо включить или исключить [Этот параграф написан для SunOs. Многие версии команды tar не содержат отдельных или даже всех из описанных возможностей. Некоторые версии выполняют все по-другому. Просмотрите свое руководство по команде tar или воспользуйтесь командой GNU tax (19.06), которую мы записали на компакт-диск. — JPJ Для отслеживания зависимостей между файлами, входящими в программу, команда make (28.13) создает в некоторых системах имена файлов, начинающиеся с запятой (,). Различные редакторы создают файлы резервных копий с именами, заканчивающимися знаком процента (%) или тильдой (~). Я часто храню первоначальную копию программы с расширением orig, а старые версии — с расширением old. Резервное копирование файлов 305
20.08 Очень часто мне не хочется делать резервные копии этих файлов. Среди них могут быть двоичные файлы, которые я не считаю нужным ни архивировать, ни удалять. Решение состоит в применении опции X команды tar (20.01). [Справьтесь в своем руководстве по команде tar, есть ли у нее также опции Fn FF. — JIK] Опция X определяет, что аргументом команды tar является имя файла, в котором перечислены файлы, исключаемые из архива. Приведем пример: % find project ! -type d -print | \ egxep '/,|%$|~$|\.old$|SCCS|/core$|\.0$|\.orig$' > Exclude % tar cvfX project.tar Exclude project Здесь команда find (tzoi) выводит список всех файлов в каталогах, но не определяет явно имена каталогов. Если имя каталога есть в списке исключений, то из данного каталога будут исключены все файлы. Команда egrep qj.os) используется далее как фильтр для получения имен файлов, не включаемых в архив. Аргументом этой команды является регулярное выражение, соответствующее требуемым именам файлов. Это регулярное выражение на первый взгляд может показаться сложным, но оно становится простым, если вы понимаете назначение специальных символов. / Косая черта не является специальным символом. Но поскольку ни одно имя файла не может содержать косую черту, она соответствует началу имени файла, поступающего с вывода команды find, а сочетание /, обозначает имена файлов, начинающиеся с запятой. I Вертикальная черта разделяет регулярные выражения. $ Знак доллара является одним из двух якорей регулярного выражения и определяет конец строки или, как в данном случае, имени файла. Другим якорем, определяющим начало строки, является символ А. Но поскольку мы сравниваем имена файлов, выводимые командой find, единственными именами, которые могут соответствовать шаблону Л, являются имена каталогов верхнего уровня. \ . Обычно точка в регулярном выражении может соответствовать любому символу. Здесь мы хотим сравнивать с настоящим символом . (точка), вот почему для изменения его обычного назначения используется обратная косая черта. Ниже приведены перечень шаблонов и примеры соответствующих им имен файлов. Шаблон /, %$ -$ \.old$ SCCS /core$ \.о$ \.orig$ Соответствует файлам С именами, начинающимися с С именами, заканчивающимися С именами, заканчивающимися С именами, заканчивающимися Из каталога SCCS С именем core С именами, заканчивающимися С именами, заканчивающимися г на на на на на о о ~ .old . 0 .orig Типы файлов Файлы зависимостей, созданные командой make Резервные файлы редактора textedit Резервные файлы редактора emacs Старые копии Файлы системы управления исходными текстами (го.п) Файлы дампа (sj.oi) Объектные файлы Файлы первоначальных версий Вместо того чтобы указывать, какие файлы нужно исключить, с помошью опции -/ можно задать, какие файлы нужно архивировать. Как и в случае использования опции X, задание имени каталога сообщает команде tar, что нужно включить весь каталог. Обратите внимание, что синтаксис команды в случае использования опции -/ отличается от стандартного. В следующем примере архивируются все файлы программ, написанных на языке С. Оператор 306 Часть третья. Работа с файловой системен
20.08 группирования () команды egrep "заставляет" ее применять символ $ ко всем шаблонам, находящимся внутри скобок: % find project -type f -print | \ egrep '(\.[ch]|[Mm] akef ile)$' > Include % tax cvf project.tar -I Include Я предлагаю использовать команду find для создания файла, содержащего список включаемых или исключаемых файлов. Если хотите, можете его отредактировать. Учтите, что дополнительные пробелы в конце любой строки приведут к игнорированию соответствующего файла. Одним из способов отладки вывода команды find является использование устройства /dev/null (13.Ы) в качестве выходного файла: % tar cvfX /dev/null Exclude project Архивирование нескольких каталогов Иногда нужно создать архив нескольких каталогов. В частности, вам может понадобиться архив каталога исходных текстов и, скажем, такого каталога, как /usr/local. Естественный (однако неверный) способ осуществления этого выглядит следующим образом: % tar cvf /dev/rmt8 project /usr/local При использовании команды tar никогда не следует указывать имя каталога, начинающееся с символа косой черты (/). Это может вызвать проблемы при восстановлении каталога (м.щ, в чем вы убедитесь позже. Правильное решение состоит в использовании флага -С: % tar cvf /dev/rmt8 project -C /usr local При этом каталог /usr/local/... будет архивироваться как local/... . Дополнительная информация по данному вопросу содержится в параграфе 20.10. Точно набирайте путевые имена При извлечении файлов из архива путевые имена, перечисленные в файлах включения или исключения, должны точно соответствовать путевым именам файлов на ленте. Предположим, что я извлекаю файлы из архива appe.tar. Приведенный ниже пример, естественно, относится и к лентам: % tar tf appe.tar appe code/appendix/font_styles.с code/appendix/xmemo.с code/appendix/xshowbitmap.с code/appendix/zcard.с code/appendix/zcard.icon Далее я создаю файл исключений exclude, содержащий следующие строки: code/appendix/zcard.с code/appendix/zcard.icon Теперь я выполняю следующую команду tar. % tar xvfX appe.tar exclude x appe, 6421 bytes, 13 tape blocks x code/appendix/font_styles.c, 3457 bytes, 7 tape blocks x code/appendix/xmemo.c, 10920 bytes, 22 tape blocks Резервное копирование файлов 307
20.09 x code/appendix/xshowbitmap.c, 20906 bytes, 41 tape blocks code/appendix/zcard.с excluded code/appendix/zcard.icon excluded Исключите архивный файл! При архивировании текущего каталога (.) не забудьте, что в первой строке файла Exclude должно указываться имя архива, создаваемого командой tar, а во второй строке — имя самого файла Exclude. Такой прием предупреждает архивирование командой tar собственного вывода! % cat > Exclude ./somedir.tar ./Exclude |CTRL-d| % find . -type f -print | \ egrep ' /, | %$ | -$ | \. old$ | SCCS | /core$ | \. o$ | \. orig$' »Excluda % tar cvfX somedir.tar Exclude В этом примере мы, чтобы ускорить создание файла, применили команду cat > (2S.oi). Но вместо нее можно использовать и текстовый редактор. Обратите внимание, что путевые имена в файле Exclude начинаются с . /. Как раз этого ожидает команда tar, когда вы просите ее архивировать текущий каталог (.). В длинной командной строке fmd/egrep используете^ оператор >> (и.ор для добавления в конец файла Exclude других путевых имен. Можно также вместо добавления в файл Exclude имен файла архива и самого файла Exclude переместить эти два файла куда-нибудь за пределы каталога, который будет читать команда tat - ВВ, TOR 20.09 Когда программа не понимает метасимволов При извлечении файлов из tar-архива удобно было бы использовать шаблон. Специальные символы шаблона необходимо защищать (8.U) от интерпретатора shell, с тем чтобы они передавались непосредственно команде tar. Однако некоторые версии команды tar не понимают метасимволов. На этот случай существует ужасно "корявый", но все же позволяющий выбирать нужные файлы прием. Попробуйте применить такую команду '...'9.16 % tar xvf /dav/rstO "tar tf /dev/rstO | grep 'шаблон'' Команда tar использована здесь дважды. Команда tar t выводит имена всех файлов на ленте. Шаблон в команде grep обеспечивает выбор нужных файлов, и результирующие имена файлов передаются первой команде tar, которая и извлекает файлы из архива. - TOR 20.10 Избегайте полных путевых имен при использовании команды tar Использование большинства версий команды tar сопровождается возникновением одной и той же проблемы: при восстановлении файлов команда не может менять путевые имена. Пусть, скажем, вы поместили в архив (ленточный или какой-либо другой) содержимое вашего начального каталога с помощью примерно такой команды: % tax с /home/mike Какие имена получат эти файлы, когда будут восстановлены в системе? Им будут присвоены те же путевые имена, какие файлы имели первоначально. Поэтому каталог /home/mike, если он уже создан, будет разрушен. Не существует способа указать команде tar, что нужно быть осторожной при переписывании файлов, как и невозможно указать, что нужно помешать файлы в какой-нибудь другой каталог. Задействуя при создании ленточного архива полные путевые имена (м.02), вы попадаете в ловушку. Воспользовавшись относительными путевыми именами (н.о2) (например, tar с .), вы сможете восстановить файлы в любой каталог, какой 308 Часть третья. Работа с файловой системой
20.11 только пожелаете. [Команда GNU tar (имеется на компакт-диске) по умолчанию преобразует полные путевые имена в относительные. Однако большинство других версий tar этого не делают, поэтому я бы не советовал полагаться на данную возможность. — JP] Это значит, что вам нужно: • избегать использования полных путевых имен при создании архивов (см. ниже); • проверять с помощью команды tar t, какие файлы есть на ленте, до их восстановления; • использовать команду GNU tar, которая может игнорировать ведущие символы / при извлечении файлов. Например, вместо команды tar с /home/mike можно выполнить команду, подобную следующей: % cd /home/mike % tar с . А можно выбрать еще более элегантный способ, воспользовавшись опцией -С: «. % tar с -С /home/mike . Указанная опция заставляет команду tar перед созданием архива перейти в каталог /home/mike. Если вы хотите архивировать несколько каталогов, можете использовать несколько опций -С % tar с -С /home/mike ./docs -С /home/susan ./test Эта команда архивирует каталог docs пользователя mike и каталог test пользователя susan. -ML 20.11 Располагайте аргументы команды tar в правильном порядке Командная строка tar для многих является одной из загадок UNIX. Трудно соотнести аргументы с опциями. Скажем, вам нужно задать размер блока (опция Ь), имя выходного файла (опция У) и имя файла исключений (опция X). Где в командной строке можно поместить всю эту информацию? Объединить опции и вставить их в командную строку (tar cXbf) достаточно просто. Но в каком месте указать размер блока, имя файла исключений и т.п.? Перечислите все необходимые аргументы после блока опций. Вы должны расположить аргументы в том же порядке, что и опции, к которым они относятся (рис. 20.1). % tar с} Cbf keepout 20 archive.shar * . txt "l" Г ' 1 l 1 Рис. 20.1. Опции и вргументы коивнцы tar В представленной на этом рисунке строке аргумент keepout соответствует опции X, аргумент 20 — опции Ь, а аргумент archive.shar — опции / Если поставить опции в другом порядке, то и порядок следования аргументов также следует изменить (рис. 20.2). % tar cbfX 20 archive, shar keepout *. txt TT 1 1 Рис. 20.2. Та же командная строка, что и на рис. 20.1, но переупорядоченная Резервное копирование файлов 309
20.12 Обратите внимание, что файлы, которые вы хотите поместить на ленту (или убрать с нее) всегда указываются в конце командной строки. Они являются не аргументами опции с или X а самостоятельной частью командной строки. Команда dump и ряд других команд работают аналогичным образом. - ML 20.12 Защита файлов с помощью SCCS и RCS Представьте себе, что вам не нужно просить системного администратора о восстановлении файла. Когда файл вам понадобится, вы сможете сразу же его получить. Более того, вы сможете восстановить любую версию файла с помощью единственной команды и также немедленно получить ее. Здорово, не так ли? Все, что вам нужно для этого, — это приобрести либо SCCS (Source Code Control System — система управления исходными текстами), либо RCS (Revision Control System — система управления версиями). SCCS становится стандартом для большинства UNIX-систем на базе System V, a RCS доступна для большинства систем на базе BSD UNIX и записана на нашем компакт-диске. Конечно, SCCS и RCS не защитят от аварии на диске, но во многих случаях они могут предупредить случайное удаление или порчу файла. Эти инструментальные средства были разработаны для управления процессом разработки коллективных проектов и дают возможность модифицировать файл только одному сотруднику, а также позволяют восстановить любую предыдущую версию файла. Как выяснилось, последнее свойство имеет большое значение для отдельных пользователей, часто изменяющих свои файлы. В параграфе 20.13 описано, как использовать для защиты своих файлов SCCS. В параграфе 20.14 аналогичная информация дана относительно RCS. - ВВ, TOR 20.13 Использование SCCS Возможно, кто-то думает, что изучить SCCS очень сложно, но это не так. Предлагаем вашему вниманию краткое введение в SCCS. 1. Создайте подкаталог с именем SCCS в каталоге, где вы храните исходные тексты программ или другие текстовые файлы, которые необходимо защитить. 2. Вставьте строку %W% %G% в содержимое файла, который хотите поставить под контроль системы SCCS. Поместите ее в комментарий (в программе на С нужно записать /* %W% %G% */, а в сценарии интерпретатора shell — # %w% %G%). 3. Поставьте файл под контроль системы SCCS. Это делается с помощью команды % асса create файл Готово. Есть еще три команды, которые нужно знать: % aces get файл % веса edit файл % «сея delta файл В список псевдонимов команд оо.о2) можно добавить определения, перечисленные ниже: С shell kah, baeh alias Create 'sees create' alias Create='sccs create' alias Get 'sees get' alias Get='sccs get' alias Edit 'sees edit' alias Edit='sccs edit1 alias Delta 'sees delta' alias Delta='sccs delta' Команда get позволяет получить копию файла. Файл будет помечен как предназначенный только для чтения (22.02). Если его необходимо отредактировать, воспользуйтесь командой edit. Закончив работу, верните файл в каталог SCCS с помощью команды delta. При каждом сохранении файлу присваивается новый номер версии. 310 Часть третья. Работа с файловой системой
20.14 Наряду с описанными есть еще две команды, знать которые также будет не лишним. Если вы выбрали файл для редактирования, а затем передумали с ним работать, воспользуйтесь командой % sees unedit файл а если хотите получить список файлов, отобранных на данный момент, задайте команду % sees check Использование SCCS — лучший способ оградить себя от неприятностей. Эта система не требует наличия нескольких десятков лент. Гораздо проще набрать % sees get -rl.12 файл Одна команда — и версия 1.12 восстановлена. Если это не та версия, восстановите предыдущую или следующую после только что полученной. Не стоит беспокоиться о том, что вы храните на диске 12 версий файла и при этом используется много дискового пространства. SCCS записывает имеющиеся в версиях различия особым образом, что позволяет ей восстанавливать любую версию файла за один его просмотр. Предположим, что вы случайно удаляете файл. Ладно. Если файл был только что выбран с помощью команды get, значит, он предназначен только для чтения, поэтому удаление такого файла заставит команду гт запросить вашего подтверждения на удаление. Если вы все-таки удалите файл, то его можно будет восстановить с помощью другой команды get. Допустим, вы выбрали файл с помощью команды edit, поскольку собирались его редактировать. Когда вы случайно удалите этот файл, то последние изменения будут утрачены. Вот почему файл нужно сохранять в SCCS по нескольку раз на день. Делайте это после внесения существенных изменений, запомнить которые трудно. Внесение изменений без регистрации в SCCS может плохо закончиться. Удачи вам! Пусть вам никогда не доведется случайно удалить важный файл. [Существует еще несколько систем управления версиями, в том числе и RCS (20.н), которая достаточно широко используется и есть на нашем компакт-диске. Относительно того, какую систему лучше использовать, вы можете посоветоваться с коллегами. Ознакомьтесь также с книгой Applying RCS and SCCS издательства O'Reilly & Associates. — JP\ - BB 20.14 Использование RCS [Я написал данный параграф по образцу параграфа об SCCS (2о.щ Брюса Барнетта. Здесь (gi ] показано, как выполнять описанные выше операции с помощью системы управления версиями (RCS), которая есть во многих системах, а также на компакт-диске. — JP] Вы можете восстановить какую угодно версию файла, воспользовавшись одной командой и не прибегая к помощи системного администратора. Приведем краткое введение в RCS. 1. Создайте подкаталог с именем RCS в каталоге, где хранятся исходные тексты программ или другие текстовые файлы, которые вы хотите защитить. 2. Не помешает вставить (хотя это необязательно) строку $Id$ в содержимое файла, который вы решили поставить под контроль системы RCS. Эту строку поместите в поле комментария (в программе на С нужно записать /* $Id$ */, а в сценарии интерпретатора shell или в Perl-сценарии — # $Id$). 3. Поставьте файл под контроль системы RCS. Это делается посредством команды % ci файл Программа регистрации ci запросит у вас краткое описание файла. Готово. Есть еще одна команда и опция, которые нужно знать: % со файл % со -1 файл Резервное копирование фатов 311
20.14 Команда со получает копию файла из системы RCS. Файл будет помечен как предназначенный только для чтения. Если его необходимо отредактировать, воспользуйтесь командой со -I. Закончив работу, верните файл в каталог RCS с помощью команды ci. При регистрации файла команда ci запрашивает у вас краткое описание внесенных изменений. Оно может оказаться очень полезным впоследствии — при изучении истории версий с целью поиска той, которую нужно восстановить. Такого рода информацию выдает команда rlog файл. Есть также и две дополнительные команды, которые вам нужно знать. Если вы выбрали файл для редактирования, а затем передумали это делать, рекомендуем воспользоваться командами % гее -и файл % хш файл а если хотите получить список файлов, выбранных на данный момент времени, задействуйте команду % rlog -L -R RCS/* (Если вы часто используете систему RCS, то можете сохранить эти команды с помощью псевдонимов или функций (io.oi) с такими именами, как Checkout, Checkedout и т.д.) Если у вас еще нет RCS, то рекомендуем приобрести ее. Это лучший способ оградить себя от неприятностей. Система не требует использования десятков лент. Гораздо проще набрать % со -rl.12 файл Одна команда — и версия 1.12 восстановлена. Если это не та версия,- восстановите предыдущую или следующую после только что- полученной. (Если вы не хотите создавать файл, добавьте опцию -р, чтобы направить содержимое файла на стандартный вывод. Вы можете направить вывод команды со по каналу другой программе — для постраничного вывода, на принтер и т.д.) Не стоит беспокоиться о том, что вы содержите на диске 12 версий файла и при этом используется много дискового пространства. RCS хранит только различия между версиями, а не все 12 копий файла, что, однако, не помешает ей достаточно быстро восстановить любую версию. Предположим, что вы случайно удаляете файл. Ладно. Если файл только что выбран с помощью команды со, он будет восстановлен и помечен как предназначенный только для чтения, поэтому его удаление заставит команду гт запросить у вас подтверждение на удаление. Если файл все-таки будет удален, вы сможете восстановить его, воспользовавшись другой командой со. Допустим, что с помощью команды со -I вы выбрали файл, который собирались изменить. В результате случайного удаления будут утрачены последние изменения. Вот почему сохранять файл в RCS нужно как можно чаще. Делайте это после внесения наиболее существенных дополнений, когда запомнить все изменения будет трудно. Объем этого краткого обзора не позволяет нам описать многие возможности системы и привести всю имеющуюся по этому вопросу информацию. Но мы не можем не упомянуть о некоторых из наиболее интересных особенностей системы. В частности, RCS может: • объединять результаты работы двух и более сотрудников в одном файле с помощью команд rcsmerge и со -j; • строить дерево версий со многими ветвями и ответвлениями, что позволяет создавать и хранить несколько независимых версий; • присваивать каждой версии некоторый статус, например alpha (альфа), released (выпущена), stable (стабильная); • именовать все версии и обращаться к ним не по номеру, а по имени; • вести список пользователей, которым разрешено манипулировать определенным RCS-файлом. 312 Часть третья. Работа с файловой системой
20.15 rcsrevs Дополнительную информацию о RCS можно найти на страницах руководства по данной системе. Наиболее полный обзор дается на /иаи-странице rcsintro(l). Многие полезные возможности системы подробно описаны на таких /иая-страницах, как с/(1). И, наконец, книга Applying RCS and SCCS издательства O'Reilly & Associates буквально напичкана советами и описанием приемов работы с системами управления версиями при разработке групповых проектов (когда они особенно нужны). В параграфах 27.10 и 20.15 представлены инструменты поиска RCS-файлов. Сценарий rcsmore (а также rcsless и rcspg), описанный в параграфе 25.04, облегчает просмотр RCS-файлов. - JP, ББ 20.15 Как получить список номеров версий RCS с помощью сценария rcsrevs Сценарий rcsrevs сообщает номера всех версий, которые хранятся в RQS-файле (2о.щ. Например: % rcsrevs myprog 1.3 1.2 1.1 1.2.1.1 Зачем это нужно? Ответим на данный вопрос, дав описание нескольких возможных ситуаций. 1. Команда rcsgrep -a (2т.щ использует сценарий rcsrevs для поиска всех версий RCS-файла. Он выдает номера версий, которые впоследствии можно обрабатывать в цикле (9.п, 9.12). Эту информацию можно использовать при выводе на печать содержимого всех версий файла или выполнении каких-либо действий со всеми версиями. Цикл, приведенный ниже, перебирает все номера версий и сохраняет их один за другим в переменной revnum. В цикле выполняется команда со -р (2о.ы), предназначенная для передачи каждой версии файла команде pr-h (43.07), которая, в свою очередь, форматирует каждый файл и добавляет к нему заголовок. Вывод команды направляется на принтер. '._' 9.16 $ fox revnum in rcsrevs файл" > 9.13 > do > со -p -r$revnum файл | рг -Ь "файл revision #$revnum" done | Ipr 45J3 > done | lpx 2. Теперь предположим, что нужно сравнить две последние версии для нескольких RCS-файлов, однако номера версий у файлов разные. (Номер последней версии одного файла может быть 2.4, а другого — 1.7 и т.п.) Последовательность действий может быть следующей. Сначала с помощью команды head (25.20) получите два последних номера версии для файла, имя которого сообщает сценарий rcsrevs. Затем посредством команды tail -r (25.IS) переставьте местами номера версий (номер более старой версии будет на первом месте). После этого используйте редактор sed для превращения номеров версий в пару опций -г (например, -rl.6 -rl.7). Теперь вы можете запустить команду rcsdiff для выполнения сравнений и отправить результат шефу (bigboss) с помощью утилиты email (1.ззу. % foreeach file (*.cc *h Makefile) ? 9.13 ? set revs=~xcsrevs $f | head -2 | tail -r | sed 's/A/-r/'' ? rcsdiff $revs $f | mail -s "changes to $file" bigboss ? end Сценарий rcsrevs использует те же опции, что и команда rlog. Так, команда rcsrevs -r2 file выводит список номеров версий начиная с версии 2.0, а команда rcsrevs -sbeta перечисляет версии со статусом beta. - JP вное копирование файлов 313
21 Еще о работе с файлами 21.01 Всякая всячина Как это ни странно, но даже такая свободно организованная книга, как настоящая, содержит материал, который, на первый взгляд, не совсем соответствует ее тематике. Данная глава посвящена вопросам, которым мы пока не удосужились уделить внимание. Эта глава короткая — уже поздно и я даже не в состоянии написать введение, которое, по идее, обязан предоставить в подобной ситуации. Просто читайте. - TOR 21.02 Прекрасное место для хранения временных файлов: каталог /tmp Сколько раз вы выполняли команду наподобие % grep foo bar > baz забывая удалить файл baz, и он затем "путался" в вашем каталоге, засоряя его и занимая дисковое пространство? И у меня такое случалось не раз. Поэтому я начал создавать свои временные файлы в системном каталоге /tmp, предназначенном специально для этой цели. Все работающие в системе имеют право записывать свои файлы в данный каталог. И поскольку со временем здесь скапливается достаточно много файлов, то для их наименования нужно постараться использовать имена, не конфликтующие между собой pi.oi). Если доступ к вашему файлу ограничен (22.04, 22.02), другие пользователи системы не смогут его читать или модифицировать. Но они смогут переименовать или удалить файл <2з.Ю), если только в правах доступа к каталогу /tmp не установлен sticky-бит (22.щ. Делать из этого проблему не стоит, но вам следует знать, что подобное может случиться. Как правило, системы удаляют оставленные в каталоге /tmp файлы или ежедневно, или один раз в несколько дней, когда файловая система переполняется, или, по крайней мере, когда система перезагружается. Поэтому не помещайте в каталог /tmp файлы, которые нужно некоторое время хранить. В вашей системе для содержания временных файлов могут быть предназначены и другие каталоги, например /usr/tmp (21.04), который очищается не так часто. Вы можете узнать об этом у своего системного администратора. - JP 21.03 Уникальные имена для временных файлов Каталог /tmp (21.02) используют все, поэтому вам нужно позаботиться об уникальности имен своих файлов. Лучше всего это сделать, поставив в имеНи символы $$. Например: % vi /tmp/jerry.$$ "/tmp/jerry.12345" (New file] % lpr /tmp/jerry.$$ % rm /tmp/jerry.$$ 314 Часть третья. Работа с файловой системоч
21.03 Интерпретатор shell меняет $$ на свой идентификатор процесса (PID) (ЖОЗ) (в данном случае — 1234 5). Если вы работаете в порожденном интерпретаторе shell (З8.04), зарегистрировались в системе более одного раза или работаете в нескольких окнах и хотите совместно с другими пользователями получить доступ к одному и тому же временному файлу, то символы $$ вам не помогут. В подобном случае следует выбрать уникальное имя. Для этой цели можно использовать текущую дату. Чтобы предоставить себе возможность использовать оба варианта формирования имен файлов при минимуме усилий, добавьте приведенные ниже строки в свои файлы конфигурации интерпретатора shell (2.02). Левый столбец содержит строки для cs/г-подобных версий интерпретатора, а правый — для s/i-подобных. . cshrc: .profile: set tf=/tmp/jp$$ tf=/tmp/jp$$ '...' 9.16 [nl 47.05 15.02 .login: set date = ('date') setenv TF /tmp/jp$date[4] export tf set 'date' TF=/tmp/jp$4 (В последних двух строках из вывода команды date (51.10) берется четвертое слово — текущее время.) Когда мне нужен временный файл из текущего каталога, я набираю % grep foo bar > $tf-l % grep wheeze bar > $tf-2 % more $t£-* Интерпретатор shell заменяет переменную (вм) $tf-l именем файла типа /tmp/jp2345-l, a переменную $tf-* — именами всех моих временных файлов, определенных в данном интерпретаторе shell. Обычно это прекрасно срабатывает. Но временные файлы для порожденных интерпретаторов shell, созданные с помощью переменной $tf, будут иметь другие имена, поскольку идентификаторы процессов различаются. Когда мне нужно обращаться к одним и тем же файлам, я использую переменную среды (6.oi) $TF. Ее значение устанавливается при регистрации в системе. И поскольку переменные среды передаются в порожденные процессы, имя файла (например, /tmp/jp09:34:56) останется в этих процессах прежним: % somepxog > $TF-1 [..| 15.02 Щ . logout, tijotout % otherprog > $TF-6 % sh $ head $TF-[16] Co временем мы обычно забываем, что в каком файле находится. Поэтому я оставляю для себя специальную заметку в переменной интерпретатора shell xf л и в переменной среды XFn, где xf означает "explain file" (файл с объяснениями), а л равно 1, 2 и т.д., что зависит от номера файла. Когда я забываю что к чему, то вывожу список путем передачи вывода команд set (для переменных интерпретатора shell), prinenv или епу (6.oi) (для переменных среды) по каналу команде grep. Например: % sort -t: +2 $tf-2 > $tf-3 % set xf3='sorted list of chapter 21 files' спустя некоторое время... % set I grep xf xfl sorted list of chapter 20 files xf3 sorted list of chapter 21 files % lpr $tf-3 Чтобы уничтожить временные файлы, нужно добавить несколько строк в конец файла .logout. Команды для интерпретатора Bourne shell (имеются на компакт-диске) выглядят подобным образом, но чтобы они работали во всех версиях этого интерпретатора, необходимо выполнить дополнительные действия. 1 о работе с фатами 315
21.04 # Удаление файлов (при их наличии) из каталога /tmp nonoinatch 15.04 set nonomatch -d ISM set tmpf="'ls -d $tf-* $TF-* |& grep -v ' not found'-" |& 13.05 if ( "$tmpf" =~ ?* ) then ' echo; echo "Your files in /tmp:" Is -d $tmpf %<9II echo -n "'rm -rf. them? [ny] (n) " =- 4704 if ( "$<" =- У* ) rm -rf $tmpf endif Если временные файлы созданы в регастрационном интерпретаторе shell или в любом порожденном интерпретаторе, то при выходе из системы я получу следующее сообщение: % logout Your files in /tmp: /tmp/jp2345-l /tmp/jp2345-2 /tmp/jp2748-l /tmp/09:23:45-1 'rm -rf' them? у Удалить эти файлы можно также с помощью сценария de[ (Н.об). - JP 21.04 Зачем нужны каталоги /tmp и /usr/tmp [По традиции в UNIX для размещения временных файлов используются два каталога: /tmp и /usr/tmp. В этом параграфе рассказывается, почему так получилось. — JP] Как я себе это представляю, причина разделения /imp — /usr/tmp та же, что и в случае разделения /bin — /usr/bin и /lib — /usr/lib, и связана она со случайной конфигурацией одной из первых исследовательских систем. В свое время одна из исследовательских систем использовала машину PDP 11/45, оснащенную дисководом с фиксированной головкой, а также несколькими дисками RK05 и RP03. Ядро находилось на диске с фиксированной головкой, и поскольку время поиска для этого диска было ничтожно малым, то ядро работало достаточно быстро. Но дисководы с фиксированной головкой (помнит ли их кто-нибудь?) были малого объема. Считалось, что 2 Мб для такого дисковода — это очень много. Нужно было соблюдать осторожность, чтобы не переполнить корневую файловую систему, содержащую каталог /tmp (он не являлся отдельной файловой системой). Каталог /usr представлял собой файловую систему и располагался на диске RP03 емкостью 40 Мб. Итак, было четкое разделение по аппаратному обеспечению: каталоги в корневом каталоге, такие как /tmp, /bin и /lib, были.быстродоступными, но небольшими, а каталоги в каталоге /usr могли быть большими, но доступ к ним был медленным. Поэтому часто применяемые команды помещались в каталог /bin, часто используемые библиотеки — в /lib, а (вот оно!) маленькие временные файлы — в каталог /tmp. Все остальные файлы попадали в подкаталоги /usr, в том числе в каталог /usr/tmp, предназначавшийся для хранения больших временных файлов. Вот почему некоторые программы, например sort w.oij,. помещают свои временные файлы в /usr/tmp — эти файлы могут быть большими. [Несмотря на то, что большинство современных файловых систем /usr являются быстрыми, многие UNIX-системы все еще отводят гораздо больше пространства для каталога /usr/tmp, чем для /tmp. — JP] На сегодняшний день дисководы с фиксированными головками стали историческими реликвиями, следовательно, большая часть причин для разделений типа /х — /usr/x исчезла. И все же одна причина, по которой разделение /tmp — /usr/tmp нужно сохранять, осталась. Если ваша файловая система /tmp находится на виртуальном диске в оперативной памяти (или на чем-то аналогичном, выбранном в целях повышения быстродействия), то при наличии в тестовом редакторе системы аварийного восстановления (30.24) вам следует хранить временные файлы редактора где-нибудь в другом месте. Система.аварийного восстановления, конечно, работает гораздо лучше, когда необходимые ей файлы сохраняются в неразрушаемой памяти! — HS, из телеконференции net.unix в Usenet, 19 марта 1984 г. 316 Часть третья. Работа с файловой системой
21.07 21.05 Чем полезна информация о времени последнего доступа к файлу UNIX сохраняет для каждого файла три временные характеристики: время последней модификации, время последнего изменения индексного дескриптора и время последнего доступа. Далее перечислены операции, которые можно выполнить, зная время последнего доступа. • Поиск забытых файлов. Для его осуществления необходимо задействовать такие команды, как Is -lu (16.02) и find -atime +180 (V.os). (Если вы пользуетесь почтовой системой МН, то можете попытаться найти таким образом сообщения электронной почты, которые давно не просматривались.) Дисковое пространство можно сэкономить, удалив неиспользуемые файлы (см. параграф 23.19). • Автоматическое сжатие файлов с помощью программы gzip (24.07), позволяющее значительно сэкономить дисковое пространство. Некоторые пользователи запускают сценарий сот- presser, предназначенный для поиска неисполняемых файлов, к которым не было доступа в течение последних 90 дней. Сценарий запускает для этих файлов программу gzip: -perm -100 17.15 find каталог! каталог? -type f ! -name '*.gz' ! -perm -100 -atime +90 -print I \ xaiss 9.21 xargs gzip -v С помощью подобных сценариев можно архивировать файлы на ленту и удалять их. Такие сценарии могут использоваться, в частности, вместе со списком пропускаемых файлов и каталогов. • Просмотр каталогов с целью поиска файлов, прочитанных программами, компиляторами и т.д. Подобная "санитарная проверка" помогает отладить программы, позволяя подтвердить наличие доступа к файлам. Примечание: Некоторые UNIX-системы, в том числе версии BSD и SunOS, не обновляют время доступа к исполняемым файлам (программам) при их запуске. Чтобы проверить, делают они это или нет, выполните команду Is -lu для исполняемого файла (не сценария) до и после запуска. - JP 21.06 Время изменения индексного дескриптора В старом руководстве по команде Is сказано, что команда Is -с выводит "время создания файла". Не верьте этому! Это старый миф UNIX. Система UNIX, как мы уже говорили, сохраняет для каждого файла три временные характеристики: время последней модификации (mtime), время последнего доступа (atime) (21.0S) и (не путать со временем создания!) время последнего изменения индексного дескриптора (1.22) (ctime). Параметр ctime изменяется при модификации файла или когда изменяется содержимое индексного дескриптора (количество жестких ссылок, имя владельца, группа и т.д.). Чтобы определить время последнего такого изменения, используйте команду Is -Icr или find -ctime (n.os, n.o7). Многие серверы проверяют параметр ctime, с тем чтобы решить, резервные копии каких файлов нужно сделать. - JP 21.07 Установка времени модификации с помощью команды touch Как можно быстро создать файл (прежде всего при выполнении какого-либо тестирования)? В интерпретаторе Bourne shell для этой цели предназначена команда, приведенная ниже. Поскольку данная команда использует встроенный оператор (но), то работает она быстро и эффективно. Команда создает новый файл или делает пустым существующий. $ > фаЛя иЦе о работе с файлами 317
21.08 ■«a Создать пустой файл в интерпретаторе С shell можно лишь путем копирования в него фяа^ /dev/null (24.01). Проще всего создать пустой файл с помощью команды touch. Посредством данной команды в любом интерпретаторе удобно также изменить время модификации существующего файла на текущее время, без изменения содержимого (обычно такой прием применяется при необходимости выполнить автоматическое сравнение времени модификации файлов (п.08,21.13,28.09)). Причем вы можете за раз обработать несколько файлов — достаточно ввести команду % touch файл! файл2 ... Некоторые версии команды touch (в том числе GNU-версия, записанная на компакт-диске) <g} 1 могут создавать файл с произвольной временной меткой. Это значит, что команду touch можно использовать для создания файла с уже прошедшей датой (или, точно так же, — с touch будущей датой). Если ваша версия может это делать, то ее синтаксис, скорее всего, таков; % touch дата файя1 файя2 ... причем аргумент дата имеет следующий вид: modyhrmiyy где то — две цифры, указывающие месяц; dy - две цифры, задающие число; hr - две цифры, представляющие часы (24-часовой формат); mi — две цифры, представляющие минуты; уу — две цифры, указывающие год (в двадцатом столетии). Последние цифры являются необязательными, и если они опущены, то предполагается, что дата относится к текущему году. Чтобы создать файл, датированный, например, 20 марта этого года, 4 часа пополудни, введите следующую команду: % touch 03201600 foo Если вы не указываете временную метку (т.е. используете текущее время), а имя файла начинается с цифры, то команда touch может решить, что имя файла представляет время, и пожаловаться: date: bad conversion (неправильное время). Чтобы создать файл с именем, начинающимся с цифры, используйте относительное путевое имя, начинающееся сточки (Щ. Например, создать в текущем каталоге файл 123456 можно с помощью команды % touch ./123456 В параграфе 22.16 описана программа cpmod (имеется на компакт-диске), предназначенная для копирования временных характеристик и прав доступа из одного файла в другой. - ML, JP 21.08 Переменные MAILCHECK и mail пригодны не только для простой проверки почты При соответствующем конфигурировании системы можно заметить, что время от времени на экране появляются сообщения типа You have mail (получена почта). Если вы запустите почтовую программу о.зз), то обнаружите почту в своем почтовом ящике. Использование этой функции позволяет контролировать изменения в нескольких почтовых ящиках, а также периодически проверять наличие изменений в файлах и каталогах, не содержащих почты. Для попьзоватепей интерпретатора С shell Если вы используете интерпретатор С shell, то такую проверку можно осуществить с помощью переменной mail, устанавливаемой, как правило, в файле .cshrc. 318 Часть третья. Работа с файловой системой
21.08 Интерпретатор shell обычно проверяет почтовый ящик через каждые пять минут. Однако вы можете установить другой интервал, указав его в начале списка параметров. Следующая команда, например, заставляет интерпретатор проверять почтовый ящик через каждые 60 секунд: % set mail=(60 /usr/spool/niail/mikel) Обратите внимание, что точное имя файла зависит от конфигурации почтовой системы. Например, многие системы используют каталог /usr/mail, а не /usr/spool/mait. Проверка наличия почты занимает время и в загруженных системах может задержать появление строки приглашения. Не устанавливайте без особой надобности короткие интервалы такой проверки. Несколько почтовых ящиков Многим пользователям приходится следить за несколькими почтовыми ящиками. Я, например, слежу за /usr/spool/mail/mikel. Но если я отвечаю за сопровождение продукта компании и моя компания поддерживает специальный почтовый адрес, по которому можно направлять вопросы, связанные с сопровождением, то я должен также следить за файлом /nsr/spool/mail/prodsupport. Чтобы облегчить себе задание, мы установили переменную mail таким образом, что она содержит список важных файлов и каталогов: % set mail=(/usr/spool/nail/mikel /usr/spool/mail/prodsupport) Когда в списке более одного файла, то интерпретатор shell сообщает, какой файл изменился, примерно так: new mail in /usr/spool/mail/prodsupport. Слежение за другими файлами Переменная mail только отображает наличие изменений в файле, не зная, что следит за "почтовым" файлом. Следовательно, ей можно приказать следить за изменениями в каком угодно файле. Список может содержать даже каталоги. Пусть, скажем, вы запускаете программу, которая периодически записывает данные в файл /home/los/mikel/radio/log.out. Тогда переменную mail можно установить следующим образом: % set mails(/home/los/mikel/radio/log.out другие_файлы) Слежение за каталогами Следить за изменениями в каталогах — это все равно, что следить за изменениями в файле. Вы будете извещены, если в каталоге произойдут изменения (будет добавлен или удален файл). Предлагаем несколько модифицировать наш предыдущий пример. Пусть, например, ваши отчеты сохраняются под именем /home/los/mikel/radio/log/дата, где дата указывает на дату создания отчета. Таким образом, для каждого нового отчета создается новый файл. В этом случае нужно следить за созданием новых файлов в каталоге log. % set mail=(/honie/los/mikel/radio/log другие_файлы) Вот еще один пример. Предположим, вы заподозрили, что кто-то с помощью утилиты UUCP (1.зз) пересылает секретную информацию вашей компании в систему с именем somewhere. Вам нужно внимательно проследить за UUCP-трафиком. Чтобы это сделать, прикажите интерпретатору shell информировать вас о каждом изменении файла регистрации: % set mail=(5 /usr/spool/uucp/.Log/uucico/somewhere) Мы приказали интерпретатору shell проверять файл регистрации через каждые 5 секунд, поскольку, подозревая наличие проблем с защитой данных, хотим получать сообщения немедленно. Каталог, за которым вы следите, на самом деле может оказаться символической ссылкой as.m) на другой каталог, поэтому убедитесь, что проверяется именно каталог, а не ссылка. Для этой цели вам подойдет сценарий s[ (is.os), а можно воспользоваться и командой Is -Id (1б.щ: % Is -Id /usr/local/logs lrwxrwxrwx 1 root 15 Jul 10 1990 /usr/local/logs -> /foo/bar/logs % Is -Id /foo/bar/logs drwxrwxr-x 2 root 512 Aug 10 1990 12:20 /foo/bar/logs % set mail=(/foo/bar/logs) [e о работе с фашин 319
21.09 Цля попьзоватепей интерпретатора Bourne shell А теперь предположим, что вы являетесь пользователем Bourne shell, и повторим проделанные операции еще раз. Интерпретатор Bourne shell для управления процессом выдачи сообщений касающихся почты, использует три переменные. (Они обычно устанавливаются в пользовательских файлах .profile am). Чтобы переменные были доступны в порожденных интерпретаторах (защ их нужно экспортировать (export) (6.oij.) Далее мы будем продвигаться немного быстрее' предполагая, что вы прочитали изложенный выше материал по интерпретатору С shell. Прежде всего, если вы хотите проверять только один файл в каталоге, установите соответствующим образом переменную MAIL: $ MAIL=/usr/spool/mail/mikel $ export MAIL Примечание: Следующие три свойства работают только в некоторых версиях интерпретатора Bourne shell. По умолчанию интерпретатор Bourne shell проверяет файлы через каждые 10 минут. Чтобы осуществлять проверку с другим интервалом, установите значение переменной MAILCHECK равным длительности нового интервала (в секундах). Приведенная ниже команда, например, просит интерпретатор shell проверять файлы через каждые 55 секунд. $ MAILCHECK=55 Если вы установите значение MAILCHECK равным 0, то проверка будет производиться при каждом выводе строки приглашения (по умолчанию — $), т.е. после выполнения каждой команды. Правда, в загруженных системах это может привести к замедлению работы. Если нужно следить за изменениями, происходящими в нескольких файлах, используйте переменную MAILPATH. Она должна содержать список файлов или имен каталогов, разделенных точкой с запятой: $ MAILPATH=/usr/spool/mail/mikel:/usr/spool/mail/prodsupport Если переменная MAILPATH установлена, то интерпретатор shell будет игнорировать переменную MAIL. Использовать их одновременно нельзя. Обычно интерпретатор Bourne shell выводит сообщение You have mail, как только изменяется файл, за которым он следит. Однако если в переменной MAILPATH после имени файла добавить знак процента (%) и текст сообщения, то при появлении изменений интерпретатор будет выводить данное сообщение. Давайте, например, заставим интерпретатор shell выводить сообщение You have mail, когда приходит почта, и сообщение New log!, когда изменяется файл регистрации: $ MAILPATH=/usr/spool/mail/mikel:/home/mikel/Z/log%"New log!" Вы можете задать для каждого файла, состояние которого отслеживаете, свое сообщение. Обратите внимание, что интерпретаторы Korn shell и bash перед каждым текстом сообщения используют вместо знака процента % вопросительный знак ?. - ML 21.09 Автоматическое обновление отчетов по файлам с помощью утилиты make Многие считают, что утилита make (жгз) предназначена исключительно для программистов. Однако она очень удобна и для тех пользователей, которым нужно знать, когда файлы изменяются. 320 Часть третья. Работа с файловой системой
21.10 Я расскажу вам о файле makefile, гарантирующем получение отчетов по последним версиям файлов в определенном каталоге. В любой момент, когда вы предполагаете, что файлы изменились, введите команду % make print pr chapl chap5 I lpr -Pxyz touch print Утилита make, увидев, что файлы chapl и chapS со времени последней распечатки изменились, использует для их следующей распечатки команды рг и lpr. Затем она запускает команду touch (21.07) для создания пустого файла с именем print. Время создания этого пустого файла указывает, когда были распечатаны файлы chapl и chapS. Команда же make printall'будет выводить на печать все файлы, независимо от времени их изменения, причем без обновления файла print. Приведем файл makefile. Измените имена и команду print таким образом, чтобы делалось то, что нужно вам. Помните, что все командные строки (здесь — строки, начинающиеся с рг и touch) должны начинаться с символа [TAB]: LPR = lpr -Pxyz /ф) 1 FILES = preface chapl chap2 chap3 chap4 chap5 appendix nu>ke_print print: $ (FILES) pr $? | $(LPR) touch print printall: pr $(FILES) I 5(LPR) - JP 21.10 Список файлов всегда отображается в верхней части экрана: сценарии dirtop Использование при очистке каталога команды Is позволяет увидеть, какие там есть файлы. При работе в среде X Window я могу держать список, полученный с помощью команды Is, в одном окне, а выполнять команды очистки — в другом. На текстовых терминалах ч использую сценарий с именем dirtop. Он очищает экран, помещает вывод команды Is в верхнюю часть экрана и посылает на терминал Escape-последовательность (s.os), которая предотвращает прокрутку этой части экрана. Поэтому вводимые мной команды размещаются под списком файлов, не смещая его. После выполнения команд, которые переустанавливают экран, например команды vi, я опять задаю команду dirtop. Чтобы вернуться к обычному режиму вывода информации на экран, я с помощью команды dirtop -с очищаю экран и восстанавливаю исходные параметры. Этот сценарий работает только на терминалах типа VT100 и совместимых с ним, поскольку Escape-последовательности здесь являются жестко закодированными. Сценарий, наверное, можно переписать таким образом, чтобы он использовал информацию из базы данных termcap или terminfo; это обеспечило бы его работу на других терминалах. К счастью, существует множество терминалов и коммуникационных программ, совместимых с VT100. #! /bin/sh И ls="/bin/Is -CF" # Используемая команда Is ■™p maxlines=10 # Ограничение на количество строк в списке, файлов # Удалите знак комментария для вашей системы: echo 46.10 cmd=echo c='\c\' e='\033' n= # SysV #cmd=/usr/5bin/echo c='\c' e='\033' n= # SunOS echo...33' 45.3S #cmd=/bin/echo c= e="'echo e / tr e '\033',M n=-n # BSD Еще о работе с файлами 321
21.11 сазе "$1" in -с) $cmd $n "${е}[r$(e}[2J${c}"; exit 0;; # Сброс экрана "") ;; *) echo "Usage: 'basename $0" [-c]" I>s2; exitl esac temp=/tmp/DIRTOP$$ trap 'rm -f $temp; exit' 0 1 2 15 $ls > $temp # Задание количества очищаемых строк: на одну больше # общего количества строк в списке команды Is. \\.у 45.31 lines="expr 1 + Vwc -l <$temp\'' if [ $lines -gt $maxlines ] then echo "'basename $CT : Directory listing > $maxlines lines" 1>S2 exit 1 else # Очистка экрана. Установка непрокручиваемой области. $cmd $n "${e}[2j$(c}" $cmd $n "5(e)[${lines};24r$(с)" # Перемещение курсора в левый верхний угол и вывод списка. $cmd $n "$(e}[0;0f${c)" cat $temp exit fi - JP 21.11 Безопасное удаление, перемещение и копирование (» гт 'S1 ср, т По умолчанию такие утилиты UNIX, как rm, mv и ср, делают то, что им поручается, без вывода запроса на подтверждение. В частности, они могут переписать или удалить все файлы из каталога. Желая обеспечить большую степень безопасности, вы можете добавить в файлы конфигурации интерпретатора shell <2.ю) определения псевдонимов этих команд: С shell Jcsh, bash alias rm /bin/rm -i alias rm='/bin/rm -i' alias cp /bin/cp -i alias cp='/bin/cp -i' alias mv /bin/mv -i alias mv='/bin/mv -i' (Путевое имя /bin в вашей системе может быть другим. Иногда его можно вообще опустить, но нужно будет проследить, не возникают ли бесконечные циклы выполнения команд, заданных псевдонимами.) После того как вы наберете команду ср или mv, будет выведен запрос на уничтожение существующего файла. А команда гт будет запрашивать такое подтверждение перед удалением каждого указанного вами файла. (Если файлов много — это может занять некоторое время!) В этих командах используется опция -/' (интерактивный), которую имеют большинство версий команд ср, mv и гт. GNU-версии команд ср и mv также имеют опцию -/'. К тому же у них имеется и альтернативная ей опция -Ь, которая автоматически создает резервную копию любого переписываемого файла. Попытка использовать псевдонимы команд rm, mv и ср в системах, где они не определены, может привести к печальным последствиям. В результате смены интерпретатора shell или регистрации в системе под другим именем вы можете уничтожить файлы, прежде чем поймете, что данные команды выполняются без предупреждения. Поэтому я не использую ни один из этих псевдонимов и перед каждым нажатием клавиши [ENTER] проверяю, что набрал. 322 Насть третья. Работа с файловой системой
21.13 Вы сможете использовать не псевдоним, а системную команду, если наберете в командной строке следующее: % \пп *.о С shell $ command rm *.o bash $ /bin/rm *.o Bourne shell - JP 21.12 Копирование файлов в заданный каталог Некоторые команды, в частности ср и mvy позволяют копировать файл в указанный каталог. Так, команда % ср filel somewhere при условии, что somewhere является каталогом, копирует файл filel в этот каталог, оставляя его имя неизменным. В результате получается новый файл, имеющий относительное путевое имя (i.2i) somewhere/file 1. АналогичнЪ ведут себя и многие другие команды, в том числе mv и In. Но иногда возникают опасения: а что если каталог somewhere не существует? Ведь вы же могли забыть его создать или неправильно набрать имя. Команда ср не подозревает, что вы, на самом деле, имеете в виду каталог, и просто копирует filel в новый файл somewhere в текущем каталоге. Существует множество ситуаций, когда это может привести к большой путанице и даже (если не повезет) возникновению ошибок. Однако у вас на этот случай имеется простой способ защиты, заключающийся.в том, что при копировании файла в каталог после его имени добавляются косая черта и точка (/.): % ср filel лутъ_к_каталогу/. Если каталог somewhere отсутствует, то появится сообщение об ошибке: % ср filel somewhere/. ср: somewhere/.: No such file or directory - ML 21.13 Чтение содержимого индексного дескриптора с помощью программы stat Программа stat читает содержимое индексного дескриптора о.щ и выводит информацию о файле (каталоге, гнезде и т.д.), которую возвращает системный вызов stat{2), а именно: временные характеристики atime pi.os), mtime (I6.02), dime (21.06) и время, прошедшее после их изменения. Например: % is -1 ptco -r-xr-xr-x 2 jerry 3203 Mar 24 05:33 ptco % stat ptco File: "ptco" Size: 3203 Allocate Blocks: 8 Filetipe: Regular File Mode: (0555/-r-xr-xr-x) Uid: (115/jerry) Gid: (100/staff) Device: 7,18 Inode: 172255 Links:2 Access: Fri May 8 01:00:30 1992(00000.05:48:13) Modify: Tue Mar 24 05:33:43 1992(00045.00:15:01) Change: Fri May 8 06:48:42 1992(00000.00:00:02) - JP ШЩе о работе с файлами 323
Автоматическое добавление даты к имени файпа Считаю, что описанный ниже трюк можно отнести к категории дурацких, но все-таки нахожу его использование иногда полезным. Для создания и редактирования файлов с датой в качестве части имени я предлагаю простую команду vid. Особенно удобной она оказалась при работе с отчетами и заметками на память. Определение команды выглядит так: alias vid "vi \!:l.'date +%m.%d~" Если данная команда будет вызвана с аргументом memo или status, то она создаст файл с таким же именем, добавив к нему дату в качестве расширения. Используя метасимволы можно затем вывести список всех своих заметок, относящихся к указанной дате или определенной теме: % is status* status.02.18 status.03.10 % Is *3.10 budjet.03.10 status.03.10 Если бы я захотел добавить в имя файла год, то в команде date нужно было бы поставить точку (или другой разделитель) и аргумент %у после нее. Изучив механизм подстановки результатов выполнения команды с помощью обратных кавычек (9.16) и форматы команды date (si.w), вы сможете разрабатывать версии этого псевдонима, работающие со всеми командами, кроме vi. - TOR
22 Управление доступом к файлам 22.01 Введение в понятия "принадлежность файла" и "защита файла" Поскольку UNIX является многопользовательской системой, в ней должны быть предусмотрены способы защиты пользователей друг от друга. Вам же не хочется, чтобы кто-то заглядывал в ваши файлы в поисках компромата, самовольно использовал только вам принадлежащую информацию, совершал другие нетактичные поступки. Даже если вы работаете в однопользовательской системе, механизм владения файлами все равно уместен, поскольку он, в частности, способен уберечь вас от удаления важных программ. В настоящей главе мы поговорим о таких понятиях, как "принадлежность файлов" и "владелец файлов". Вы узнаете, как изменить принадлежность файла, задать те или иные права доступа к нему и т.д. Мы также обсудим некоторые способы предотвращения "подсматривания", в том числе поговорим о шифровании и очистке экрана. На мой взгляд, большинство брешей в системе защиты связано с возникновением ошибок, которых можно легко избежать: кто-то может узнать, например, что любой пользователь может читать электронную почту шефа. Ознакомившись с данной главой, вы поймете, как избежать типичных ошибок и защитить себя от "незваных гостей". - ML 22.02 Краткое руководство по правам доступа к файлам и каталогам [Вы считаете, что о правах доступа знаете все? Даже если это так, просмотрите данный параграф — Брюс дает ряд очень дельных советов. — JP] Имеется три атрибута прав доступа: чтения, записи и выполнения. Право на чтение и запись, очевидно, позволяет читать данные из файла и записывать новые данные в файл. Имея право на выполнение, вы можете использовать файл как программу. Для обозначения этих атрибутов используются буквы г, w и х соответственно. При работе с каталогами используются те же атрибуты прав доступа, но они приобретают другое назначение. Если вы имеете право на чтение каталога, то можете посмотреть, какие файлы содержатся в нем. Право на запись означает, что вы можете добавлять, удалять или переименовывать файлы в каталоге. Право на выполнение позволяет использовать имя каталога при обращении к хранящимся в нем файлам.* (Дополнительная информация о том, что может содержаться в каталоге, имеется в параграфе 18.02.) Поговорим обо всем этом более детально. Предположим, у вас есть право на чтение каталога, но нет права на выполнение. В таком случае вы можете читать содержимое каталога или информацию из индексного дескриптора (1.22) каталога, возвращаемую системным вызовом stat(2). Это значит, что можно видеть имена файлов, права доступа к ним, информацию об их размере, времени доступа и количестве связей, а также имена владельцев и групп владельцев. Однако содержимое файлов читать нельзя. Это значит также, что вы можете войти в каталог с помощью команды ccl. — Примеч. ред. Управпение доступом к фатам 325
22.02 Право на запись в каталог позволяет изменять его содержимое. Поскольку имя файла хранится в каталоге, а не в файле, то право на запись в каталог позволяет создавать, переименовывать и удалять файлы. Например, если кто-то имеет право на запись в ваш начальный каталог, то он может переименовать или удалить ваш файл .login, поместить на его место другой файл. При этом права доступа к вашему файлу .login значения не имеют. Переименовать файл можно даже в том случае, если нет права на чтение его содержимого (см. параграф 22.11). Право на выполнение для каталога иногда называется правом на поиск. В каталоге, предоставляющем право на выполнение (но не на чтение), вы можете использовать любой файл, но лишь при условии, что знаете его имя. Нельзя определить имя файл, заглянув в каталог. В этой связи можно провести аналогию с "черным ящиком": вы, конечно же, можете попытаться угадать имя файла, но это не всегда возможно (см. параграф 22.12). Пользователь, группа и остальной мир Принадлежность файла определяется такими категориями, как владелец и группа. Есть три набора атрибутов прав доступа для чтения, записи и выполнения: один набор — для владельца, второй — для группы пользователей (22.П), третий — для всех остальных. Эти атрибуты задаются девятью битами в индексном дескрипторе и представляются в списке, который выводит команда Is -l, символами rwxrwxrwx:* % Is -1 drwxr-xr-x 3 jerry books 512 Feb 14 11:31 manpages -rw-r—r— 1 jerry books 17233 Dec 10 1990 misc.Z -rwxr-xr-x 1 tim books 195 Mar 29 18:55 myhead Первый символ в списке, выведенном командой Is -l, определяет тип файла (П.п). Первые три из девяти атрибутов прав доступа, идущие дальше, относятся к владельцу, средние — к группе, три последние — ко всем остальным. В том случае, если атрибут не установлен, ставится дефис, означающий отсутствие данной привилегии. Если у вас есть файл данных, который можно читать и модифицировать, но вы не хотите, чтобы кто-либо другой имел к нему доступ, то строка атрибутов должна выглядеть так: rw . Проще всего задать эти девять битов при помощи трех восьмеричных цифр, заменяющих девять символов. (В параграфе 1.23 приведен рисунок, на котором изображены биты прав доступа, а также объясняется, как можно записать права доступа в виде восьмеричного числа.) Порядок следования сохраняется, поэтому перечисленные выше права доступа могут быть представлены восьмеричным числом 600. Первая цифра определяет права владельца, вторая — права группы, а последняя — права каждого, кто не является владельцем и не входит в группу [впрочем, права доступа не относятся к суперпользователю (1.24), который может делать с файлом или каталогом что угодно. — JP]. При проверке прав доступа система поочередно просматривает группы атрибутов. Если категории пользователей, к которой вы относитесь, отказано в доступе, то UNIX не станет анализировать следующую группу атрибутов. Рассмотрим случай, когда права доступа к файлу, принадлежащему пользователю jo из группы guests, заданы в виде xrwx, или 017 в восьмеричном коде. В результате пользователь jo не может работать с этим файлом, любой член группы может выполнять его как программу, а все, кроме пользователя jo и членов группы guests, могут читать, модифицировать и выполнять эту программу. Это не совсем обычный набор прав доступа, но некоторые применяют подобный механизм (22.14) при необходимости предотвратить доступ или использование файла определенной группой пользователей. В описанном выше случае пользователь jo не может читать или модифицировать файл, который ему принадлежит. Он мог бы воспользоваться командой chmod (22.07), с тем чтобы предоставить себе право на чтение файла. Однако если файл находится в каталоге, принадлежащем кому-то другому, и этот каталог не предоставляет пользователю jo право на чтение или поиск, то данный пользователь не сможет найти файл и изменить права доступа к нему. * Обратите внимание: в системах Berkeley UNIX команда Is -I выводит восьмиколоночный список без имени группы (здесь — books). Чтобы получить такой же формат списка, как показан здесь, используйте команду Is -lg. 326 Часть третья. Работа с файловой системой
22.02 Приведенный выше пример не типичен. В большинстве случаев права доступа можно разбить на следующие четыре категории. 1. Информация является личной. Многие имеют один-два каталога, в которых записывается информация, не предназначенная для общего доступа. Конфиденциальной, в частности, может быть почта, поэтому файлы почтового ящика должны иметь код доступа 700, запрещающий читать письма всем, кроме вас и суперпользователя (см. параграф 4.05). 2. Информация не является личной, однако никто не должен иметь права ее изменять. Для большинства моих каталогов права доступа заданы в виде числа 755. 3. С файлами работает группа людей. Это предполагает наличие группового права на запись и установку права доступа к каталогу в виде числа 775. 4. Если проект является конфиденциальным, может быть нежелательным доступ сотрудников, не входящих в группу. Тогда можно создать каталоги с кодом доступа 770. Предположим, вы только что создали каталог с определенными правами доступа и поместили в него файлы в надежде, что они надежно защищены. Но это не всегда так. Если у вас есть каталог с правом доступа 755, а в каталоге хранится файл с правом доступа 666, то любой пользователь может изменить содержимое этого файла, поскольку все "прочие" имеют право на поиск в данном каталоге и право на запись в данный файл. Для подобных случаев необходим механизм, обеспечивающий невозможность модификации новых файлов всеми пользователями. Такой механизм существует в виде команды umask (ИМ). Если вы не считаете, что новый каталог должен иметь право доступа 777, а новые файлы — право доступа 666, то команда umask будет задавать те атрибуты прав доступа, которые нужно убирать из всех создаваемых файлов. Чтобы убрать право на запись для категории "прочие", нужно из исходного кода 666 вычесть 002 (получится 664). При необходимости убрать право на запись для прочих и для группы пользователей нужно вычесть 022, чтобы получить значение 644. Эти два значения параметра umask настолько часто используются, что есть смысл определить их с помощью псевдонимов (io.o2y. alias open umask 002 alias shut umask 022 При таких значениях параметра umask новые каталоги будут иметь право доступа 775 или eshjnit, 755. Большинство пользователей выбирают одно из этих двух значений. В дружном коллективе сотрудников для параметра umask, как правило, устанавливается значение 002, позюляющее другим членам группы вносить изменения в файлы. Тот, кто использует значение 022, будет вызывать нарекания со стороны других сотрудников, работающих над проектом. Попытка компиляции программы не удастся, если кто-то другой владеет исполняемым файлом, который будет перезаписан при компиляции. В подобном случае вы можете переименовать такой файл или попросить помощи у системного администратора. Члены коллектива, которые обычно используют значение 022 параметра umask, должны при работе над проектом найти способ изменения этого значения. (В противном случае они рискуют быть опозоренными своими же сотрудниками!) Кроме приведенного выше псевдонима open, некоторые пользователи создают псевдоним для команды смены каталога и установки значения параметра umask, обеспечивающего право на запись для группы: alias proj "cd /usr/projects/proj;umask 002" Это не идеальное решение, потому что многие забывают о существовании такой команды. Можно определить псевдоним с именем cd для команды смены каталога и специальный файл в каждом каталоге проекта, который устанавливает значение параметра umask при переходе в такой каталог. Другие сотрудники могут иметь в каталоге проекта аналогичные файлы с иными именами. В параграфе 14.14 показано, как это сделать. Еще один способ предполагает запуск три раза в день команды find (izoi). Команда ищет в каталоге проекта принадлежащие вам файлы с некорректными правами доступа: SUSER 6.03 % find /usr/projects -user $USER ! -perm -020 -print | \ xaras 9.21 xargs chmod g+v <hma& 22.07 \ Управление доступом к файлам 327 ®
22.03 Задать время выполнения этой команды можно с помощью команды crontab -e (шг, тлц. [Если в вашей системе персональные файлы crontab не создаются, воспользуйтесь возможностью автоматического запуска задания посредством команды at (40.Щ. — JP] К какой группе относится новый файл Наличие права на запись для группы пользователей — обязательное условие функционирования коллективного проекта. Интересно, а каким образом определяется, какой группе будет принадлежать новый файл? Ответ зависит от ряда факторов. Но прежде чем я расскажу об этом, хотелось бы отметить, что в системах Berkeley и AT&T применяются различные механизмы задания группы по умолчанию. Первоначально новая группа в UNIX задавалась при помощи команды newgrp. Если для этой группы существовал пароль в файле /etc/group, а имя пользователя в списке членов группы отсутствовало, то последний должен был ввести пароль, чтобы поменять свою группу. Berkeley-версии UNIX для определения группы нового файла используют текущий каталог. Это значит, что если текущий каталог принадлежит фуппе cad, то к ней должен относиться любой файл, создаваемый в этом каталоге. Чтобы изменить фуппу, устанавливаемую по умолчанию, достаточно перейти в другой каталог. Оба механизма имеют свои достоинства и недостатки. Механизм, реализованный в Berkeley-версиях, позволяет изменять группу автоматически. Однако здесь имеется ограни- ченное количество фупп, к которым можно принадлежать. В SunOS 4 такоьых 16, а в более ранних версиях — всего лишь 8. SunOS и System V Release 4 поддерживают оба механизма. Если нужно управлять доступом на уровне каталогов, то используется специальный бит прав доступа (n.os). Новые файлы в каталоге с установленным специальным битом будут принадлежать той же фуппе, что и каталог. Без специального бита фуппа всех новых файлов будет зависеть от текущей фуппы пользователя. - ВВ 22.03 Кому будет принадлежать новый файл? Если вы используете файлы совместно с другими пользователями, неплохо было бы знать, кому принадлежит каждый файл. В системах на базе BSD это имеет особое значение, поскольку здесь только суперпользователь имеет право изменять владельца файла (22.20,22.21). Правила определения принадлежности файла сформулированы ниже: 1. Создав новый файл, вы становитесь его владельцем. 2. Принадлежность файла, дополненного путем задействования оператора » файл, не меняется, поскольку новый файл при этом не создается. 3. Принадлежность файла не меняется и в результате его переименования с помощью команды mv. Однако если вы используете команду mv для перемещения файла в другую файловую систему (1.22), то такой файл будет принадлежать вам, поскольку при этом, по сути, команда mv копирует файл и уничтожает оригинал. 4. При копировании файла копия принадлежит вам, поскольку ее создаете вы (22.ii). 5. При редактировании файла нужно помнить о следующем. — Если вы это делаете с помощью редактора W (зом), то файл сохраняет исходную принадлежность, поскольку новый файл вообще не создается. — В случае редактирования с помощью редактора Emacs (32.01), создающего резервные копии, владелец может быть разным. Резервная копия будет принадлежать либо вам, либо первоначальному владельцу. При замене отредактированного файла его резервной копией принадлежность файла может измениться: % emacs filea ...Длительное редактирование, а затем отказ от изменений... % mv filea- filea Если сомневаетесь в этом, введите команду Is -I (22.02). - JP 328 Часть третья. Работа с файловой системой
22.05 22.04 Установка точного значения параметра команды umask С помощью команды umask для создаваемых файлов можно задать права доступа, устанавливаемые по умолчанию. Ее аргументом служит трехразрядное восьмеричное число, представляющее биты прав доступа, которые нужно запретить, или маскировать. Таким образом, ожидаемое этой командой значение является восьмеричным дополнением нужного кода доступа. Чтобы получить это значение, определите числовой эквивалент о.23) права доступа к файлу, а затем вычтите его из числа 777.* Например, чтобы установить по умолчанию значение прав доступа равным 751, произведите несложный подсчет (в восьмеричной системе): 777-751=026. Это значение нужно предоставить команде umask. % umask 026 После выполнения этой команды все создаваемые файлы автоматически получат определенный уровень защиты. Системные администраторы могут помещать команду umask в системный файл инициализации, задавая таким образом для всех пользователей права доступа, устанавливаемые по умолчанию. Вы можете задать собственное значение аргумента umask в файле конфигурации интерпретатора shell (2.oi). Дополнительная информация о команде umask и двух пользовательских командах, предназначенных для изменения значения ее аргумента, приведена в параграфе 22.02. — AF, из книги Essential System Administration издательства O'Reilly & Associates 22.05 Права доступа для группы и бит SGID При работе в многопользовательской системе можно манипулировать правами доступа для группы (22.02). Так, вы можете позволить определенной группе осуществлять запись в тот или иной каталог, запретив делать то же самое другим группам. Каким образом UNIX определяет группу, которая будет владеть создаваемыми вами файлами? Для этого имеется три способа. 1. В большинстве систем на базе System V принадлежность создаваемых вами файлов определяет действующий идентификатор группы процесса (GID). (Действующим иденти1 фикатором является первичный идентификатор группы (22.13), если только вы не выполняете программу с установленным битом SGID (1.23).) 2. В большинстве версий BSD UNIX файлы принадлежат группе пользователей, владеющей каталогом, в котором создается файл. 3. Правила, действующие в SunOS и System V Release 4, более сложные. Какой из двух предыдущих способов будет использован в той или иной файловой системе для определения принадлежности файла, решает системный администратор. Мы же посоветуем следующее. Загляните на страницу системного руководства ореп(2), где об этом рассказывается более подробно. Но, возможно, проще создать новый пустой файл (2/.07) и проверить его принадлежность с помощью команды Is -l или Is -lg (22.02). Кроме того, для управления групповой принадлежностью вы сможете использовать бит смены идентификатора группы (SGID). В таком случае, если в правах доступа к каталогу этот бит установлен, то используются правила BSD, а если не установлен — правила System V. Для установления и снятия бита SGID предназначены команды chmod g+s (22.07) и chmod g-s соответственно. При необходимости изменить группу пользователей, которой будет принадлежать файл, можно воспользоваться командой chgrp (>-23). Однако вы должны быть владельцем данного файла и, кроме того, членом новой группы. При изменении прав доступа может возникнуть ситуация, когда в списке прав доступа (получаемом с помощью команды Is -I) присутствует буква S, например drwxr-s . Что это значит? Это значит, что бит SGID каталога установлен, а бит выполнения — нет. (Часто это бывает результатом неправильных действий пользователя.) Если вы хотите, чтобы каталог стал доступен для группы, добавьте с помощью команды chmod g+x право на выполнение. - JP, ML * Следует уточнить, что для файлов нужно вычитать из числа 666, а для каталогов — из числа 777. — Примеч. ред. Управление цоступом к файлам 329
22.06 Ш 22.06 Защита файлов с помощью sticky-бита Пользователь UNIX, имеющий право на запись в каталог, может переименовывать и удалять файлы, даже если они ему не принадлежат (см. параграф 22.11). Многие новые версии UNIX имеют средства предотвращения такой возможности. Владелец каталога может установить sticky-биш (код о.гз) 1000), и тогда переименовывать и удалять файлы смогут, кроме него самого, лишь владелец файлов и суперпользователь. Приведем пример. Пользователь jerry создает каталог с правом на запись для всех и устанавливает sticky-бит (показан здесь как t): jerry% mjcdir share jerry% chmod 1777 share jerry% Is -Id share drwxrwxrwt 2 jerry ora 32 Nov 19 10:31 share Другие пользователи создают в этом каталоге свои файлы. Но пользователь Jennifer не сможет удалить файл, принадлежащий пользователю ellie: jennifer% Is -l total 2 -rw-r—г— 1 ellie ora 120 Nov 19 11:32 data.ellie -rw-r—r— 1 Jennifer ora 3421 Nov 19 15:34 data.Jennifer -rw-r—r— 1 peter ora 728 Nov 20 12:29 data.peter jennifer% rm data.ellie data.ellie: 644 mode ? у rm: data.ellie not removed Permission denied - JP 22.07 Изменение прав доступа к файлу с помощью команды chmod Изменить права доступа к файлу можно лишь путем использования команды chmod, причем для этого нужно быть владельцем файла или пользователем root. Синтаксис команды довольно прост. % chmod новий_код файл(и) Параметр новый_код описывает права доступа, которые должны быть установлены. Есть два способа задания прав доступа: либо с использованием числового кода, либо с использованием определенных символов, описывающих производимое изменение. Я, как это ни странно, отдаю предпочтение числовому коду. Так или иначе, чтобы использовать числовой код, решите, какие права доступа должны быть заданы, выразите их посредством восьмеричного числа (/.23,22.02) и введите примерно такую команду: % chmod 644 report.txt Она предоставит владельцу файла report.txt право на чтение и запись, а всем остальным — только право на чтение. Большинство пользователей предпочитают применять символьное представление прав доступа. Команда chmod с символьными операторами выглядит следующим образом: % chmod g-w report.txt Это означает, что нужно отменить право на запись для членов группы. Используемые в этой команде символьные операторы приведены в табл. 22.1. 330 Часть третья. Работа с файловой системой
2107 Табпица 22.1. Симвопьные операторы команды chmod Категория Пользователь: Действие: Права: Оператор U g 0 а - + = г W X X S t Описание Владелец файла Член группы Прочие пользователи Все (пользователь, группа и прочие) Удалить указанный атрибут Добавить указанный атрибут Установить именно эти атрибуты (22.00 Чтение Запись Выполнение Установка (или отмена) права на выполнение для каталогов или файлов, которые имели установленный бит "выполнения" хотя бы для одной категории пользователей Установка бита смены идентификатора пользователя или группы (допускается только с операторами + и -) Установка sticky-бита (1.23,22.06) Категории "Пользователь" и "Права" описаны в параграфе 22.02. Приведем примеры использования символьных операторов. о=г Установка только права на чтение для прочих пользователей, независимо от того, в какое состояние установлены другие биты. о+r Добавление права на чтение для прочих пользователей. go-w Отмена права на запись для членов группы и прочих пользователей. a=rw Предоставление всем (владельцу, членам группы и прочим пользователям) права на чтение и запись (но не на выполнение). Помните, что операторы + и - устанавливают или отменяют лишь определенный атрибут прав доступа, оставляя остальные неизменными. Например: % Is -1 foo -rwx х 1 mikel % chmod a+x foo % Is -1 foo -rwx—x—x 1 mikel % chmod o-x,g+r foo % Is -1 foo -rwxr-x 1 mikel 0 Mar 30 11:02 foo 0 Mar 30 11:02 foo 0 Mar 30 11:02 foo Обратите внимание на последнюю команду chmod. Она демонстрирует некоторые возможности, о которых мы прежде не упоминали. При использовании символьных операторов можно комбинировать две или более спецификации, разделенные запятыми. Эта команда означает, что нужно отменить право на выполнение для всех прочих пользователей и добавить право на чтение для членов группы. (ф 1 GNU-версия команды chmod записана компакт-диск. При необходимости изменить права доступа для всего дерева каталогов, т.е. для всех файлов chmod каталога и всех его подкаталогов, рекомендую использовать команду chmod -R (рекурсивное выполнение) или find -exec (izioj. И хотя эти команды применяются нечасто, они позволяют сэкономить немало времени. - ML Управление доступом к файлам 331
22.08 22.08 Оператор = команды chmod Предположим, что у вас есть группа файлов, часть из которых вы можете модифицировать а другую — только читать. Попытаемся установить для членов вашей группы такие же права доступа, как и у вас. Это легко сделать с помощью недокументированной возможности команды chmod: % chmod g=u * Данная команда означает, что для всех файлов (*) нужно установить такие же групповые права доступа (д), какие имеет владелец (и). Для обозначения прочих пользователей можно также использовать оператор о. Категории пользователей описаны в параграфе 22.02. Если ваша команда chmod имеет опцию -R, аналогичные изменения можно произвести по отношению ко всем файлам и каталогам вашего текущего каталога и его подкаталогов. Если опция -R не поддерживается, используйте такую команду find azwy. % find . -exec chmod g=u {} \; Программа cpmod (22.16), имеющаяся на компакт-диске, может копировать все атрибуты прав доступа к файлам. - JP 22.09 Защита важных файлов: запрет на запись в них Хорошим способом предупреждения ошибок является превращение некоторых файлов в такие, которые предназначены только для чтения. При попытке удалить один из этих файлов выводится соответствующее предупреждение. Такое же предупреждение будет выведено при попытке скопировать один файл в другой, защищенный-от записи. Если вы уверены в том, что действительно хотите удалить или переместить.файл несмотря на его доступность только для чтения, можете использовать с командой rm или mv опцию -/ Таким образом вы заставите -эти-команды производить изменения без вывода предупреждения. Менять права доступа к файлам вручную нецелесообразно. Для упрощения набора команд можно определить два псевдонима: # Установка режима доступа "только чтение" alias -w chmod -w # Добавление права на запись alias +w chmod +w [Эти псевдонимы действительно удобны! Правда, я использую вместо них сценарий с именами c-w и cw соответственно. При написании программ я применяю команду сх, по которой этот же сценарий выполняет команду chmod +x. Подробное объяснение принципов работы этого сценария приведено в параграфе 22.10. — JP\ Вообще-то, идея отменить право на запись в некоторые файлы достаточно удачна. Часто файлы содержат информацию, которую трудно восстановить, и хранятся вперемежку с легко восстанавливаемыми файлами. Может также возникнуть необходимость защиты некоторых редко изменяемых файлов. Сочетание таких средств, как ограничение доступа к каталогам и установка подходящих параметров команды umask (22м), позволяет найти приемлемый способ защиты тех или иных файлов. Можно легко создать сценарий, который добавляет право на запись, редактирует файл, а затем отменяет это право: #!/bin/sh # Установление права на запись в файлы chmod u+w "$@" # Редактирование файлов; использование редактора vi, # если переменная VISUAL не определена ${VISUAL=vi} "$@" # Отмена права на запись chmod -w "$@" - вв 332 Часть третья. Работа с файловой систыюй о cshjnit, sh init chmod edit "$@- 44. IS $(..=.'.} 45.12
22.11 22.10 Быстрое изменение прав доступа с помощью команд сх, cw и c-w Я часто использую простой сценарий, приведенный ниже. Чтобы обеспечить право на выполнение нового сценария, я набираю % сх файя_сценария Выполнение команды cw приводит к установке права на запись; команда c-w отменяет это право: #! /bin/sh case "$0" in *сх) chmod +х "$@" ;; *cw) chmod +w "$@" ;; *c-w) chmod -w "$@" ;; *) echo "$0: Help! Shouldn't get here!" 1>&2; exit 1 esac На сценарий имеются три ссылки. Поместите сценарий в файл с именем сх, затем введите % chmod +x сх % In сх cw % In сх c-w Сценарий проверяет имя, по которому он вызывается и которое содержится в переменной $0, чтобы решить, какую из команд chmod выполнять. Этот прием позволяет экономить дисковое пространство. Можно также добавить другие команды, вставляя строки в тело оператора case и создавая новые ссылки. Наконец, можно использовать псевдонимы (22Щ. - JP 22.11 Лазейка: модификация файлов без права на запись Никто не говорит, что UNIX — это идеальная система (i.M). Одной из ее "вечных" проблем является защита файлов. Вот один из недостатков, которого следует остерегаться: не имея права на запись в файл, вы не можете его изменить. Однако если у вас есть право на запись в каталог, попробуйте эту проблему обойти: % Is -1 unwritable -г—г—г—1 John 334 Mar 30 14:57 unwritable % cat > unwritable unwritable: permission denied % cat unwritable > temp % vi temp % mv temp unwritable override protection 444 for unwritable? у % cat unwritable John wrote this originally, an made the file read-only. But then Mike came along and wrote: I should not have been able to do this!!! # Сначала Джон написал это и сделал файл доступным только для чтения. # Но затем подключился Майк и написал: # "Я не должен был этого делать!!!" Я не мог производить запись в файл unwritable напрямую, но я смог его скопировать, а затем с помощью редактора vi внести необходимые изменения. В конце концов, у меня же есть право на чтение, а чтобы сделать копию файла, достаточно его прочесть. Имея собственную копию, я, естественно, могу ее редактировать, как мне заблагорассудится. Закончив эту операцию, я смогу с помощью команды mv записать новый файл поверх файла unwritable. Почему? Переименование файла требует только наличия права на запись в каталог. Право на запись в сам файл иметь необязательно. (Заметим, что команда ср здесь не работала бы — Управпение доступом к файлам 333
22.12 чтобы осуществить копирование, файл unwritable, если он существует, должен быть доступен для записи.) Это одна из причин, по которой за правами доступа к каталогу нужно следить очень внимательно. Как видите, предоставление права на запись в каталог другим пользователям может таить опасность. Если эта опасность для вас реальна, подберите правильные значения параметров команды umask (22.04) при создании каталогов и команды chmod (22.07) — при изменении прав доступа к существующим каталогам. Вы можете также оставить каталоги доступными для записи и установить sticky-бит (22М). - ML 22.12 Каталог доступен, но список его содержимого вывести невозможно Хотите ли вы сделать так, чтобы другие могли использовать ваши файлы, но, тем не менее чтобы никто из работающих в системе не мог "рыскать" по вашему каталогу? Можно установить для каталога право на поиск (выполнение) и запрет на чтение. В результате, если файл в этом каталоге доступен, любой пользователь сможет с ним работать, но лишь при условии, что точно наберет имя. Команда Is сообщит, что права на чтение каталога нет. Шаблонами имен файлов пользоваться нельзя. Приведем пример. Допустим, что ваш начальный каталог имеет права доступа rwxr-xr-x (все могут использовать файлы и выводить их список). Ваше имя — hanna, у вас есть подкаталог project, и вы устанавливаете права доступа таким образом, что все другие пользователи системы имеют только право на выполнение: hanna% pwd /home/hanna hanna% chmod 711 project -d /lift? hanna% ls-ld project project/myplan drwx—x—x 2 hanna 512 Jul 26 12:14 project -rw-r—r— 1 hanna 9284 Jul 27 17:34 project/myplan Далее вы сообщаете пользователю toria точное имя вашего файла — myplan. Как и любой другой пользователь системы, она имеет право доступа к вашему каталогу project. Однако toria не получит список его содержимого, поскольку не имеет права на чтение каталога, Но зная точное имя файла, toria может прочесть этот файл, поскольку его чтение разрешено (файл может прочесть каждый, кто знает его точное имя): toria% cd /home/hanna/project toria% pwd pwd: can't read . toria% Is Is: . unredable toria% more myplan ... Просмотр файла... toria% In myplan /home/toria/project.hanna/plan (Здесь используется "настоящая" команда pwd, которая просматривает файловую систему, разыскивая ваш текущий каталог. Вот почему она "жалуется", что не может прочесть каталог (can' t read .). Если вы используете псевдоним команды pwd, то, возможно, не получите сообщения, показанного выше. Более подробно об этом рассказывается в параграфе 14.04.) В приведенном выше примере toria создала в своем каталоге project.hanna жесткую ссылку <7*ед на файл myplan, причем ссылка имеет другое имя. (Пользователь toria могла скопировать файл или выполнить любую другую команду, которая читает файл.) Если вы, hanna, теперь запретите доступ к своему каталогу project кому бы то ни было, пользователь toria по-прежнему будет иметь доступ к файлу myplan. Она сможет читать его в любой момент, отслеживать внесенные вами изменения и т.д.: toria% cd toria% Is -d project.hanna project.hanna/plan 334 Часть третья. Работа с файловой системой
22.13 drwx 2 toria 512 Jul 27 16:43 project.hanna -rw-r—r— 2 toria 9284 Jul 27 17:34 project.hahna/plan toria% more project.hanna/plan . . .Просмотр файла... Пользователь toria защитила свой файл project.hanna таким образом, что другие пользователи не смогут найти ссылок на файл пользователя hanna. Примечание: Если пользователь hanna не разрешает доступ к своему каталогу, то пользователь toria все равно может прочесть файл посредством жесткой ссылки. Однако если toria создаст символическую ссылку, то получить доступ к файлу уже не сможет. Объясняется это тем, что жесткая ссылка указывает на номер индексного дескриптора файла (1.22, is.02), a символическая ссылка — нет. Возможно, вам понадобится предоставить прочим пользователям право выводить список содержимого и получать доступ к файлам каталога, но не делать каталог доступным для всех пользователей без исключения. Одним из способов осуществления этого является помещение полностью доступного каталога с необычным именем внутри нечитаемого каталога. Пользователи, знающие точное имя полностью доступного каталога, могут переходить в него, а все остальные — нет: hanna% chmod 711 project hanna% chmod 777 project/pLaN hanna% ls-ld project project/pLaN drwx—x—x 3 hanna 512 Jul 26 17:36 project drwxrwxrwx 2 hanna 512 Jul 27 17:37 project/pLaN Пользователи, которые наберут команду cd /home/hanna/project/pLaN, смогут вывести список содержимого этого каталога с помощью команды Is. С теми правами доступа, которые установлены в данном примере, другие пользователи также могут создавать, удалять и переименовывать файлы внутри каталога pLaN, хотя можно задать и более ограниченные права, например drwxr-xr-x. Такая установка по-прежнему будет запутывать. В частности, как говорилось в параграфе 14.04, прочие пользователи не смогут использовать команду pwd в каталоге pLaN, поскольку эта команда не в состоянии прочесть каталог project. Переменные cwd (шз) и PWD (б.оз), возможно, будут содержать полное путевое имя. Если пользователь заблудится в таим каталоге с ограниченными правами доступа, то лучше всего вернуться в начальный каталог и начать все заново. - JP 22.13 Группы и групповое владение Механизм разделения пользователей на группы служит важной составляющей системы защиты файлов в UNIX. Каждый пользователь может быть членом одной или нескольких групп, что определяется записями в файлах /etc/passwd (Зб.оз) и /etc/group. Чтобы выяснить, к какой группе вы принадлежите, найдите с помощью команды grep (27.01) свою запись в файле /etc/passwd: % grep raikel /etc/passwd mikel:sflghjraloweor:50:100:Mike Loukldes:home/mikel:/bin/csh [Кроме того, вы можете попробовать задействовать для этой цели команду ypcat passwd | grep mikel. — JP] Четвертое поле (второе число) является идентификатором вашей первичной группы. Ищите это число в файле /etc/group: % grep 100 /etc/group staff:*:100:root Управление доступом к файпам 335
22.14 [Вы также можете воспользоваться командой ypcat group | grep 100. — JP\ Моей первичной группой является staff. Следовательно, при входе в систему мой идентификатор группы устанавливаемся равным 100. Чтобы выяснить, к каким еще группам вы принадлежите примените команду groups, если, конечно, она имеется в вашей системе. В противном случае ищите свое имя в файле /etc/group: % grep raikel /etc/group power:*:55:mikel,jerry,tim weakness:*:60:mikel,harry,susan [Вы можете использовать и команду ypcat group I grep mikel. — JP\ Я также являюсь членом групп power и weakness с идентификаторами групп 55 и 60 соответственно. При работе в BSD UNIX мы всегда являемся членами всех своих групп. Это значит, что я, например, могу получать доступ к файлам, принадлежащим группам staff, power и weakness, не выполняя для этого каких-либо специальных действий. В System V UNIX невозможно одновременно находиться в нескольких группах, членом которых вы являетесь. (Здесь уместно провести аналогию с клубами по интересам. Вы можете входить в клубы. Elks (Лоси) или Odd Fellows (Чудаки), но одновременно надеть обе клубные шляпы вам не удастся.) При необходимости получить доступ к файлам, принадлежащим другой группе, используйте ,. команду newgrp: % newgrp имя_грушш (System V позволяет даже переходить в группу, к которой вы не принадлежите. В этом случае нужно ввести пароль группы. Пароли групп используются редко — обычно в поле пароля вводится звездочка, которая четко указывает, что для данной группы нет действующего пароля.) В большинстве систем существуют группы для основных проектов или отделов, группы системных администраторов и, возможно, одна-две группы для гостей. Некоторые системы на базе BSD имеют группу wheel, в которую всегда должен входить пользователь root (г.24). Во многих системах терминал является доступным для записи только владельцу или специальной группе пользователей с именем tty. Это предотвращает посылку другими пользователями символов на ваш терминал без использования такой известной программы с установленным битом SGID (из), как write о.зз). -ML 22.14 Цобавление пользователей в группу с целью отмены прав доступа Обычно права доступа для группы (22.13) позволяют группе пользователей получать доступ к каталогу или файлу, которые иначе были бы для них недоступными. Можно изменить это правило на обратное, организовав группы, для которых права доступа отсутствуют. Примечание: Этот прием работает только в таких UNIX-системах, которые позволяют пользователю принадлежать к нескольким группам одновременно. Одной из таких систем является BSD. В частности, вы можете работать на компьютере, в котором есть некоторые защищенные авторским правом файлы и программы, недоступные в определенных учетных записях. Все другие пользователи этого компьютера должны иметь к ним доступ. Чтобы это реализовать, поместите такие программы в каталог, принадлежащий группе с именем типа deny (отказ). Затем выполните команду chmod, что позволит вам отменить права доступа для этой группы: # chmod 705 /usr/local/somedir # Is -lgd /usr/local/somedir drwx r-x 2 root deny 512 Mar 26 12:14 usr/local/somedir Теперь вы можете зарегистрировать членов группы deny в файле /etc/group. 336 Часть третья. Работа с файловой системои
22.15 UNIX проверяет права доступа в порядке владелец-группа-прочие. Используются первые подходящие для данной категории пользователей права доступа. В нашем случае ни одна из "гостевых" учетных записей не принадлежит суперпользователю (как мы надеемся! : -)). Однако они все являются членами группы deny, поэтому соответствующие права (—) вступают в силу и члены этой группы лишаются доступа. Для прочих пользователей, не являющихся членами группы deny, устанавливаются права доступа г-х — такие пользователи могут входить в данный каталог. Та же установка работает для индивидуальных файлов (например, программ). Будьте только внимательны при изменении прав доступа к системным программам с установленными битами SUID и SGID а.23). - JP, ПК 22.15 Манипулирование правами доступа Как и- любое другое средство защиты, права доступа в UNIX могут порождать определенные проблемы. Решив позволить друзьям пользоваться своей квартирой, вы должны убедиться, что у них есть ключ. Когда же вы позволяете какому-либо пользователю работать с вашими файлами, убедитесь предварительно, что у него есть право на чтение и запись. В идеальном случае каждый файл должен был бы содержать список пользователей, которым можно с ним работать, и владелец файла мог бы по своему желанию добавлять и удалять имена пользователей из этого списка. Некоторые системы защиты UNIX имеют такую конфигурацию, но стандартные системы не обеспечивают подобного уровня регулирования прав доступа. И мы должны знать, как, манипулируя правами доступа к файлам, достичь нужной цели. Предположим, что я имею файл с именем chOl, который должен редактироваться пользователем val. Я сообщаю последнему, что полное имя файла — /books/ptools/chOl, но он мне отвечает, что не может получить доступ: val % cd /books/ptools val % more chOl chOl: Permission denied Объясняется это тем, что файл могу читать только я. Пользователь val должен проверить права доступа к файлу при помощи команды Is -/: val % is -l chOl -rw 1 lmui 13727 Sep 21 07:43 chOl При этом он просит меня (lmui) предоставить ему право на чтение и модификацию файла. Ведь только владелец файла и суперпользователь могут изменять права доступа. Каким же способом лучше всего предоставить пользователю val доступ к файлу chOP. Самый быстрый и верный способ предоставить права доступа другому пользователю — это распространить право на чтение и запись на всех: lmui % chmod 666 chOl lmui % Is -1 chOl -rw-rw-rw- 1 lmui 13727 Sep 21 07:43 chOl Но это все равно, что оставить входную дверь открытой настежь при желании позволить своему коту свободно входить и выходить из квартиры. Целесообразнее будет распространить право на чтение и запись только на членов своей группы, а не "на весь мир". Попытаюсь предоставить пользователю val доступ к файлу путем предоставления права на чтение и запись группе: lmui % chmod 660 chOl lmui % Is -1 chOl -rw-rw 1 lmui 13727 Sep 21 07:43 ChOl Управление доступом к файпам 337
22.16 I groups Однако пользователь val говорит, что это ничего ему не дало: val % more chOl chOl: Permission denied Что же случилось? Я, конечно, предоставила право на чтение и запись группе, которой принадлежит файл, но ведь пользователь val не входит в эту группу! Группу, которой принадлежит файл, можно определить с помощью опции -lg команды Is (в System V это делается по умолчанию, когда вы набираете Is -/): val % Is -lg chOl -rw-rw 1 lmui power 13727 Sep 21 07:43 ChOl Для определения того, к какой группе принадлежит пользователь, можно воспользоваться (gi J командой groups (ее GNU-версия имеется на компакт-диске): % groups val val : authors ora % groups lmui lmui : authors power wheel ora Файл chOl принадлежит группе power, членом которой пользователь val не является, но и пользователь lmui, и пользователь val входят в группу authors. Следовательно, предоставить пользователю val доступ к файлу chOl можно лишь при условии, что этот файл принадлежит группе authors. Поэтому я воспользуюсь командой chgrp а.тзу. lmui % chgrp authors chOl lmui % Is -lg chOl -rw-rw 1 lmui authors 13727 Sep 21 07:43 chOl Теперь пользователь val может читать и изменять этот файл. (В System V ему, возможно, нужно будет предварительно выполнить команду newgrp (22.13).) - LM 22.16 Копирование прав доступа с помощью утилиты cpmod Утилита cpmod облегчает манипулирование правами доступа, выполняя копирование атрибутов владения и прав доступа из одного файла в другой. Предположим, вы только что установили права доступа (22.15) с помощью команд chmod и chgrp, предоставив какому-либо пользователю доступ к файлу chOl. Однако этот пользователь просит предоставить ему право доступа еще к трем файлам в том же каталоге. Вы можете повторить описанную процедуру или же воспользоваться командой cpmod, чтобы скопировать атрибуты прав доступа из первого файла: 13727 Sep 21 07:43 ChOl 34020 Oct 15 11:13 ch02 11207 Oct 13 09:49 ch03 29239 Oct 07 18:12 ch04 13727 Sep 21 07:43 ChOl 34020 Sep 21 07:43 ch02 11207 Sep 21 07:43 ch03 29239 Sep 21 07:43 ch04 Команда cpmod означает следующее: "Сделать эти файлы подобными данному". Мы же использовали ее для того, чтобы быстро установить атрибуты разрешения записи для нескольких файлов одновременно. Заметим, однако, что новые файлы унаследовали и время модификации. Это еще одна возможность команды cpmod, столь удобная для профаммистов [..] 15.02 % Is -lg chOl -rw-rw 1 lmui % Is -lg ch0[234] -rw-r 1 -rw-r—r— 1 lmui lmui lmui % cpmod chOl ch0[234] % Is -lg chO? -rw-rw 1 -rw-rw 1 -rw-rw 1 -rw-rw 1 lmui lmui lmui lmui authors book acct book authors authors authors authors 338 Часть третья. Работа с файловой системой
22.17 и всех пользователей программы make (28.13). Эта программа на основании даты модификации файлов определяет необходимость перекомпилирования исходного текста. Команда cpmod предоставляет возможность манипулировать датами модификации. В параграфе 21.07 описана версия команды touch, с помощью которой можно установить любую дату модификации файла. - LM 22.17 Способы улучшения защиты файлов, зашифрованных программой crypt Файлы, зашифрованные с помощью программы crypt, без особых усилий могут быть расшифрованы специалистами по шифрам. На протяжении долгого времени благодаря программе, разработанной в 1986 году Робертом Болдуином (Robert Baldwin) в MIT Laboratory for Computer Science, даже люди, не занимающиеся шифрованием, могли найти ключ к сообщениям, обработанным с использованием программы crypt. Программа Болдуина, названная cbw (Crypt Breaker's Workbench — инструмент взломщика шифров), автоматически расшифровывает такие текстовые файлы в течение одной-двух минут. Программа cbw начала быстро распространятся, поэтому файлы, зашифрованные с помощью программы crypt, нельзя было считать защищенными. (Они не были таковыми и до появления программы cbw, просто более узкий круг людей обладал достаточными техническими навыками для их расшифровывания.) Мы также не рекомендуем использовать программу crypt для шифрования файлов объемом более 1 Кб. Однако у вас может не быть другой легкодоступной шифровальной системы. Если это так, то желательно воспользоваться хотя бы программой crypt. Чтобы уменьшить вероятность расшифровки своих файлов, вы должны соблюдать определенные меры безопасности. • Файл необходимо зашифровывать по нескольку раз, применяя на каждом этапе разные ключи. Это существенно усложнит характер преобразования. • Перед шифрованием файлы нужно сжимать (24.07). Информация (обычный ASCII- текст (51.03)), используемая программами для проверки правильности составления ключа шифра, при сжатии изменяется. Если ваше сообщение не будет при расшифровке преобразовано в обычный текст, то программа, например cbw, не сможет определить, правильно ли она расшифровала это сообщение. Однако если злоумышленники узнают, что вы это делаете, то смогут соответствующим образом изменить свою версию используемой программы. • Если для сжатия своих файлов вы используете программу compress или pack, трехбайтовый заголовок, состоящий из шестнадцатеричных значений If, 0d и 90 (именно в этом порядке), следует удалить. Если злоумышленник догадается, что файл был сжат перед шифрованием, то знание того, как расшифровать первые три байта, поможет ему расшифровать остальную часть файла. Эти три байта можно удалить с помощью команды dd (35му* % compress -с <plain I dd bs=3 skip=l | cript >encripted Перед распаковкой файла эти три байта заголовка, конечно же, нужно восстановить. Заголовок можно получить, воспользовавшись файлом /dev/null (iiJi4y. 0 13.07 % (compress -cf /dev/null;crypt <encrypted) | uncomress -c >plain • Если у вас нет программы compress, воспользуйтесь командой tar (i9.os), чтобы объединить свой файл, который нужно зашифровать, с некоторым другим, содержащим случайные данные, а затем зашифровать архив. Присутствие случайных данных существенно затруднит выделение осмысленного текста такими программами, как cbw. — SG, GS, из книги Practical UNIX & Internet Security издательства O'Reilly & Associates Команда dd, используемая таким образом, работает очень медленно и неэффективно. Если вам приходится шифровать большое количество сжатых файлов, напишите небольшую программу для удаления заголовка, работающую более эффективно. Управление доступом к файлам 339
22.18 22ЛВ Очистка экрана терминала для защиты информации и предотвращения его повреждения Команда clear читает запись о терминале в базе данных termcap или term info (s.02), чтобы выяснить, как должен быть очищен экран, а затем посылает команду очистки. Если вы набираете что-то конфиденциальное, что другим людям знать нежелательно, введите в строке приглашения, если есть такая возможность, команду clear. Многие программы позволяют осуществлять временный выход в интерпретатор shell 00.26) для выполнения единственной команды. Вы можете очистить экран, набрав в программе что-нибудь типа ! clear. Примечание: Некоторые терминалы и многооконные системы имеют память — буферы прокрутки, в которых сохраняются предыдущие строки. Команда clear может не очищать эти буферы. Загляните в соответствующее руководство, чтобы выяснить, как выполнить такую очистку. Если же ничего не получается, выйдите из UNIX и на минуту выключите питание терминала. А что будет, если вы покинете свой рабочий стол на время длительного совещания или даже на целый день, забыв, что экран не очищен. Попробуйте это сделать с терминала другого пользователя. (Если в системе запись разрешена только для группы tty (22.U), зарегистрируйтесь сначала в своей учетной записи, например с помощью команды su (22.22).) Если типы терминалов совпадают, воспользуйтесь командой % who | grep вадгв_имя яашл_чыя ttyp3 Jun 24 10:44 % clear > /dev/ttyp3 В случае использования терминала другого типа, прежде чем вводить эту команду, временно (б.ю) установите переменную среды TERM. - JP Сценарии интерпретатора shell должны быть доступными для чтения, но не обязательно — для выполнения Почти все знают, что программы нужно делать доступными для выполнения, иначе UNIX не станет с ними работать. Да, это верно для непосредственно выполняемых двоичных файлов, таких, например, как программы на языках С или Pascal, но для интерпретируемых программ, в частности сценариев интерпретатора shell, это не совсем так. Ядро UNIX может читать исполняемый двоичный файл напрямую, и если для пользователя установлено право на выполнение, то ядру этого достаточно; право на чтение ему предоставлять не нужно. Однако сценарий должен читаться пользовательской программой (интерпретатором shell). Для чтения файла UNIX-программа должна иметь право на чтение. Поэтому сценарии должны быть доступными для чтения. Не нужно устанавливать право на выполнение сценария, если он запускается следующим образом: % sb сценаряй % зЬ < сценарий Наличие права на выполнение является для ядра сигналом о том, что оно должно попытаться выполнить файл, если вводится только имя файла: % сцлнлряй Итак, для сценария не обязательно устанавливать право на выполнение, что очень удобно. -JP ж -' ■•■- Часть третья. Работа с файловой системой.
22.21 22.20 Почему невозможно изменить принадлежность файла в BSD UNIX? [Крис объясняет, почему в Berkeley UNIX только пользователь root (1.24) может изменить принадлежность файла. Однако если это нужно сделать вам, можно найти выход (22.21). — JP] Это ограничение не надуманно, поскольку система поддерживает дисковые квоты (24.17). Если бы вы были в состоянии менять принадлежность собственных файлов, то смогли бы сделать следующее: mkdir .hide; chmod 700 .hide cd .hide create_huge_file >foo chown profl foo create_huge_file >bar chown prof2 bar create_huge_file >baz chown prof3 baz Вам нужно только найти такого пользователя, чья квота больше (или таковой вообще нет) и который нечасто проверяет свои ресурсы, поэтому и не подозревает, что его диск на 99% заполнен какими-то неизвестными файлами. Затем нужно изменить принадлежность стольких файлов, сколько необходимо, чтобы уложиться в выделенную квоту. Вы можете восстановить принадлежность файлов, скопировав их сначала в другой раздел диска, удалив оригиналы и скопировав файлы обратно. — СТ, из телеконференции comp.unix.questions в Usenet, б июля 1989 г. 22.21 Как изменить принадлежность файла, не используя команду chown UNIX-снстемы с дисковыми квотами (24.17) не допускают замены владельца (22.20) файла, поскольку команду chown может применять только супёрпользователь. Вот как это положение можно обойти в подобных системах. 1. Текущий владелец файла должен убедиться, что новый владелец имеет право на запись в каталог, в котором находится данный файл, а также право на чтение самого файла: jerry% Is -Id . afile drwxr-xr-x -2 jerry 512 Aug 10 12:20 . -rw-r—r— 1 jerry 1934 Aug 10 09:34 afile jerry% chmod go+w . 2. Новый владелец (зарегистрированный под своим именем) должен переименовать файл, создать его копию и удалить оригинал. Проще всего зарегистрироваться под другим именем с помощью команды su (22.22/. jerry% su laura Password:- laura% rav afille afile.tmp laura?; cp -p afile.tmp afille laura% Is -1 afile -rw-r—г— 1 laura 1934 Aug 10-09:34 afile laura% rm -f afile.tmp laura% exit jerry% chmod go-w . Команда cp -p (l&.is) сохраняет первоначальные права доступа к файлу и время его последней модификации. После того как новый владелец (laura) закончит копирование, прежний владелец (Jerry) опять сможет отменить право на запись в каталог. - JP УпрабпенНе доступом к файпам 341
Z2.22 22.22 Команда su — не только для суперпользователя Системные администраторы, применив команду su, становятся суперпользователями (и^. Однако вы можете использовать ее и для других целей: • чтобы на время получить права другого пользователя, не выходя из своей учетной записи; • для входа в систему под другим именем, без перехода на другой терминал; • при необходимости в любой момент времени стать одним из нескольких пользователей (в системах с управлением заданиями); • для "быстрой регистрации" в другой учетной записи, особенно когда система перегружена. Если набрать youraccount% eu whoever Password: whoever% то UNIX запустит порожденный интерпретатор shell (З8.м) с правами пользователя whoever. После перехода (посредством команды cd) в начальный каталог этого пользователя вы можете выполнять команды так, как будто вошли в систему под его именем (правда, имеются некоторые ограничения — см. ниже). Выход ипи приостановка Для возврата в ту учетную запись, в которой вы ввели команду su, нужно задать команду exit (3S.04) или [CTRL-d]. В системах с управлением заданиями (П.о$) можно временно приостановить порожденный интерпретатор shell и вернуться в учетную запись, в которой была выполнена команда su. Чтобы это сделать, введите команду suspend, если интерпретатор shell пользователя whoever поддерживает управление заданиями (большинство версий shell его поддерживают); в противном случае наберите в командной строке комбинацию клавиш [CTRL-z]. Примечание: Если интерпретатор shell, запущенный командой su, не поддерживает управление заданиями, а ваш исходный интерпретатор поддерживает, то нажатие клавиш [CTRL-z] во время выполнения любой команды останавливает выполнение и ее, и порожденного интерпретатора shell. Воспользовавшись командами suspend и su, вы можете запустить несколько интерпретаторов shell. Из любого выполняемого при этом процесса можно вернуться в свой исходный интерпретатор, причем не будут утрачены ни перечень ранее введенных команд, ни имя текущего каталога, ни какие-либо другие параметры среды. Поскольку все эти экземпляры интерпретатора shell связаны с тем же портом #у (3.08), что и ваш исходный интерпретатор, то команда su не занимает другие порты tty или pty, как это бывает при входе в систему нескольких пользователей или при задействовании нескольких окон. Это очень удобно при работе на загруженных машинах со многими пользователями. В любой UNIX-системе в случае необходимости возвратиться в исходный интерпретатор shell можно ввести команду exit (или [CTRL-d]). А в системах с управлением заданиями с помощью команды su можно работать от имени нескольких пользователей и в любой момент возвращаться в исходный интерпретатор shell. Механизм управления заданиями позволяет приостанавливать процесс, запущенный командой su, и возвращаться в первоначальный интерпретатор shell без необходимости ввода другой команды su (и пароля). Многие версии shell имеют команду suspend, также позволяющую это делать. В других версиях интерпретатора можно ввести команду [CTRL-z] (приостановка выполнения задания) или создать псевдоним (W.04) для команды останова текущего экземпляра интерпретатора shell: Ш38.10 alias suspend='kill -STOP $$' $$ 8.19 Приведем пример. Я вошел в систему под именем jerry на компьютере wheeze. С помощью команды su я стал суперпользователем и пользователем учетных записей sarah и manuals. Для 342 Часть третья. Работа с файловой системой
22.22 О whoami, id переключения между различными учетными записями я использую механизм управления заданиями: jerry@wheeze% jobs [1] Stopped su [2] - Stopped su sarah [3] + Stopped su manual jerry@wheeze% fg su manuals ...Выполнение команд в качестве пользователя manuals manuals@wheeze% suspend Stopped jerry@wheeze% fg %1 su wheeze# ...Выполнение команд в качестве суперпользователя wheeze* suspend Stopped jerry0wheeze% Я настолько часто использую этот прием, что для выполнения команды suspend создал псевдоним (10.Ю), назвав его одной буквой — z- Кто вы теперь? Переключаться между различными учетными записями намного проще, если строка приглашения (7.oi) содержит в себе имя пользователя. Если это не так, то, чтобы узнать, правами какого пользователя вы сейчас обладаете, можно воспользоваться командой whoami или id. В вашей системе должна быть хотя бы одна из этих команд. GNU-версии обеих команд имеются на компакт-диске. Кроме того, чтобы узнать имя, под которым вы вошли в систему, введите команду who am i (с пробелами). Возможные проблемы Некоторые версии System V не изменяют значения переменной среды HOME (LOGDIK) (i4.ii) на правильное значение для той учетной записи, в которую вы перешли с помощью команды su. Это значит, что выполнение команды cd приведет к переходу в начальный каталог вашей первоначальной (а не новой) учетной записи. Кроме того, интерпретатор С shell, который вы запускаете в другой учетной записи, будет читать ваш файл .cshrc. Лучшим решением этой проблемы является создание сценария с именем su, устанавливающего указанную переменную. Сценарий предназначен для интерпретатора С shell : - ( (47.02), поскольку этот интерпретатор поддерживает оператор ~ (тильда) (i4.ii) для определения начального каталога пользователя. Поместите этот сценарий в каталог, указанный в переменной PATH перед каталогом /bin (S.oi), либо создайте собственную команду или функцию, которая станет запускать сценарий вместо стандартной команды su. #!/bin/csh -f # su - исправление некорректных значений $НОМЕ # и $USER; запуск системной команды su foreach arg ($argv) # найти первый аргумент, не являющийся опцией if ("x$arg" !~ x-*) then setenv HOME -$arg setenv USER $arg exec /bin/su $argv:q end if end echo "$0 ERROR: can't find username." exit 1 Другим решением той же проблемы является создание псевдонима для команды su. Имя псевдонима должно совпадать с именем того пользователя, правами которого вы хотите обладать: (..) 13.07 alias randi '(setenv HOME -randi; setenv USER randi; su randi) ' Ф x46. V 47. exec 45. A 9. Управление доступом к файлам 343
22.22 И еще об одной проблеме, которая может возникнуть в любой версии UNIX. Пользователь в чью учетную запись вы переходите с помощью команды su, может не иметь права (22 т\ доступа к каталогу, в котором была выполнена команда su. Поэтому вы можете получить от интерпретатора С shell сообщение об ошибке типа getwd: can't stat .. Может быть, вы и не получите подобного сообщения, но команда su не выполнится. Решением обеих проблем является переход в общедоступный каталог (например, с именем / или /Шр) перед выполнением команды su. Это легко можно сделать с помощью следующего псевдонима: \su 10.06 alias su ' (cd /; \su \!*)' При желании вы можете добавить в определение псевдонима команду cd /. Если в учетной записи, выбираемой с помощью команды su, запускается интерпретатор С shell (и вы не используете опцию -/ — см. ниже), то он будет читать ваш файл .cshrc. В этом файле могут присутствовать жестко заданные путевые имена команд, которые не всегда выполняются. Попробуйте заменить жестко заданные путевые имена типа /home/ста- рая_учетная_запись/Ып путевыми именами типа $home/bin, -/bin и т.п. Команда su имя_пользователя не позволяет читать файлы .login (С shell) и .profile (Boutne shell). Использование команды su - имя_пользователя (см. параграф "Другие возможности команды su") решает эту проблему, но приостановить интерпретатор shell, запущенный командой su -, не удается (по крайней мере, в тех системах, где мне приходилось работать). И поскольку команда su выполняется в порожденном интерпретаторе shell <жо4), то переменные, среды (6.01), установленные в учетной записи, в которой выполняется эта команда, передаются в порожденный процесс. Это и хорошо, и плохо. Например, имя вашего любимого редактора (EDITOR (б.оз)), такого как v/ или Emacs, может стать доступным в другой учетной записи. Д6 в новой учетной записи также могут быть установлены переменные, которые вы хотите использовать. При желании узнать, какие переменные были определены после выполнения команды ли, наберите set, чтобы вывести список переменных интерпретатора shell, и задайте либо команду env, либо команду printenv (6.oi), чтобы увидеть переменные среды. Быстрый переход в другие учетные записи Если ваша система сильно загружена, то на выполнение всех команд, которые содержатся в файлах xshrc других пользователей, может уйти довольно много времени. А между тем команда su может передавать аргументы в запускаемый ею интерпретатор shell. Если в других учетных записях используется интерпретатор С shell, то опция -/сообщает, что файл .cshrc читать не нужно (su -f имя_пользователя). Среда не будет сконфигурирована в соответствии с установками в файле xshrc, но вы начнете работать быстрее. Если вход в систему занимает много времени и вы хотите переключиться на другую учетную запись, можете выполнить команду su с помощью команды exec: % exec su имя_пользоватоля Это приведет к возникновению странной ситуации: команда who (si.04) будет показывать, что вы зарегистрированы в первоначальной учетной записи, но работать вы будете как пользователь имя_полъзователя (это подтверждают команды whoami и id). Кроме того, поскольку интерпретатор shell, запущенный командой su, не является регистрационным <г.щ, запустить команду logout вы не сможете. Вместо нее необходимо ввести команду exit. Итак, команда exec su капризна, но работает быстрее. Другие возможности команды su Команда su -е, которая может выглядеть как su -m или su -p, используется для перехода в учетную запись другого пользователя с сохранением текущей среды. Это удобно, если у вас имеются проблемы с параметрами среды других пользователей или если вам нужно сохранить свою среду. (Некоторые версии команды su выполняют опцию -т по умолчанию. Используйте команды env или printenv (б.оо, и вы увидите, что получится.) Команда su - имитирует полную регистрацию в другой учетной записи. Если в других учетных записях используется интерпретатор Bourne shell, то будет прочитан файл .profile. В случае интерпретатора С shell будут прочитаны оба файла — .cshrc и .login. Вы не можете приостановить работу интерпретатора shell, запущенного с помощью команды su -. - JP 344 Часть третьи. Работа с файповойокпШ
23 Удаление файлов 23.01 Время создает и время рззрушать Подобно всем пользователям вы, надо полагать, много времени тратите на создание файлов. Как неизбежной противоположностью жизни является смерть, так и обратной стороной создания файла является его удаление. Если мы вообще не станем удалять файлы, то рано или поздно в компьютере наступит нечто, напоминающее перенаселение: диски начнут стремительно заполняться и в конце концов в системе не останется свободного места. Мы вынуждены будем либо тратить деньги на покупку и установку дополнительных дисков, либо срочно выяснять, какие файлы не нужны, и удалять их. В этой главе разговор пойдет о способах избавления от файлов: какие меры безопасности следует- предпринимать; как избавиться от файлов, которые "не хотят умирать"; как найти "несвежие", или неиспользуемые, файлы, хранящиеся длительное время. Безопасное удаление — тема очень интересная, поскольку UNIX-команда rm отличается радикализмом: если уж вы удаляете файл, то он исчезает навсегда.. Существует несколько решений этой проблемы, позволяющих сохранить возможность реанимации файлов. - ML 23.02 Опасность команды rm Команда rm в UNIX используется для удаления файлов. Синтаксис команды достаточно прост: нужно набрать ггп, а затем — перечень файлов. Пожалуй, команда rm даже слишком проста. Можно легко удалить больше, чем необходимо, и если что-то пропадет, то пропадет безвозвратно. Имеется несколько приемов, которые делают команду rm более безопасной, и мы до них дойдем. Но сначала поговорим о некоторых потенциальных опасностях. Чтобы понять, почему восстановить удаленные файлы невозможно, вам необходимо знать, как работает файловая система UNIX. Система содержит список свободных блоков, в котором перечислены неиспользуемые дисковые блоки. При удалении файла удаляется его запись в каталоге (которая содержит имя файла). Если на этот файл больше нет ссылок (ы.ю) (т.е. файл имеет только одно имя), то его индексный дескриптор (1,22) добавляется в список свободных дескрипторов, а его блоки данных добавляются в список свободных блоков. Так Почему же нельзя вернуть файл из списка свободных блоков? Ведь существуют утилиты DOS, которые примерно в такой же ситуации восстанавливают удаленные файлы. Вспомните, что UNIX — это многозадачная операционная система. Даже если вы думаете, что ваша система является однопользовательской, есть вещи, которые происходят "за вашей спиной": программы-демоны записывают данные в файлы регистрации, обрабатывают сетевые соединения, электронную почту и т.д. Теоретически файл можно восстановить, если файловая система на момент удаления файла будет "заморожена", но последнее невозможно. В UNIX все и всегда активно. Пока вы сообразите, что совершили ошибку, блоки данных вашего файла могут быть повторно использованы для чего-то другого. В процессе удаления файла особую осторожность следует проявлять при использовании метасимволов. Появление обычных опечаток может привести к роковым последствиям. Me файлов 345
23.03 Допустим, вы хотите удалить все свои объектные файлы (.о). Однако из-за нервной дрожи вы добавляете лишний пробел и набираете % пп * .о Все выглядит корректно, и вы, возможно, и не заметите ошибку. А когда поймете, что она произошла, файлы текущего каталога уже будут безвозвратно утеряны. Думаете, что вам такая неприятность не грозит? Но все же послушайте, что однажды произошло со мной. Еще будучи новичком в UNIX, я работал над бизнес-планом для своей компании. Руководство посчитало, что в целях безопасности права доступа к бизнес-плану нужно установить таким образом, что для его модификации нужно быть пользователем root (1.24). (Ошибка в определении прав доступа, но это другая история.) Я использовал терминал с незнакомой клавиатурой, и совершенно случайно мне удалось создать несколько файлов с четырьмя управляющими символами в начале их имен. Чтобы их удалить, я (как пользователь root) набрал следующее: # пп ????* Эта команда выполнялась долго. Когда две трети каталога были уничтожены, я с ужасом понял, что происходит: удаляются все файлы с четырьмя и более символами в имени. И это еще не все. Оказалось, что никто не делал резервных копий на протяжении примерно пяти месяцев. (Между прочим, этот параграф должен предоставить вам массу доводов в пользу регулярного создания резервных копий (2о.о2>.) К тому времени, когда я воссоздал все удаленные файлы (сам процесс занял несколько часов — это делалось на "древней" версии UNIX с ужасной утилитой backup) и вручную сверил все файлы с печатной копией бизнес-плана, я твердо решил, что при работе с командой гт буду очень осторожным. - ML 23.03 Способы повышения безопасности команды rm А теперь несколько слов о том, как предотвратить случайное удаление файлов. • используйте команду rm -/, определив для нее, если это возможно, псевдоним (параграфы 21.11 и 23.07); • сделайте команду rm -;' более дружественной (параграф 23.06); • напишите сценарий удаления, который перемещает удаляемые файлы во временный каталог (параграф 23.08); • помните, что в интерпретаторе tcsh (8.оз) есть переменная rmstar, которая заставляет интерпретатор shell запрашивать подтверждение на выполнение команды, если вы набираете что-нибудь вроде rm *; • используйте более интеллектуальную программу безопасного удаления, наподобие описанной в параграфе 23.09; • используйте систему управления версиями (параграф 23.12); • создавайте собственные резервные копии, как описано в параграфе 20.02; • предупреждайте удаление (переименование, создание) файлов, делая каталог (не обязательно файлы в нем!) недоступным для записи (параграф 22.02). Если вы хотите удалять не задумываясь, используйте команду гт -/(параграф 23.10). - ML 23.04 На вопрос "Да или нет?" всегда отвечайте "Да" Некоторые команды, в частности rm -i и find -ok, просят пользователя ввести с клавиатуры ответ на вопрос "Делать или нет?". В этой связи можно вспомнить программу удаления файлов с именем del, которая просит разрешения на удаление каждого файла: % del * Remove filel? у 346 Часть третья. Работа с файловой системой
23.06 yes Remove file2? у Если вы даете положительный ответ (у), то файл удаляется. А что если необходимо запустить команду, которая намерена задать вам не менее 200 вопросов? На все эти вопросы 1гужно ответить утвердительно, но ведь вам не хочется каждый раз набирать ответ с клавиатуры? В таком случае передайте по каналу команде удаления вывод команды yes, которая станет отвечать вместо вас: % yes | del * Remove filel? Remove file2? При необходимости дать на все вопросы отрицательный ответ (п) введите команду % yes n 1 del * Примечание: Не все команды UNIX для получения ответов на свои вопросы читают стандартный ввод. Если команда напрямую открывает файл терминала (Jdev/tty (4S.20J) для чтения вашего ответа, то команда yes работать не будет. Попробуйте в таком случае использовать программу expect (9.26). Команда yes может отвечать не только у и п. Как проверить терминал на возможность работы с командой yes, рассказано в параграфе 42.07. GNU-версия данной команды имеется на компакт-диске. - JP 23.05 Выборочное удаление Большинство пользователей применяют команду rm -i (2i.ii) для обеспечения безопасности, получая запрос на подтверждение перед удалением каждого файла. Майк Лукидис рассказал мне о другом способе использования команды гт -/. Когда нужно удалить несколько файлов, а набирать метасимволы о.щ в обычной команде гт неудобно, Майк предоставляет команде гт -/ более широкий список и отвечает п в отношении файлов, которые он не хочет удалять. Например: % Is aberrant abhorred abnormal abominate acerbic aberrate abhorrent abominate absurd acrimonious % rm -i ab* rm: remove aberrant (y/n)? у rm: remove aberrate (y/n)? n rm: remove abhorred (y/n)? у rm: remove abhorrent (y/n)? n - JP 23.06 Более быстрый способ интерактивного удаления файлов [Команда rm -i запрашивает подтверждение на удаление каждого файла. В этом параграфе мы еще раз поговорим о том, как можно избежать столь нудного процесса ввода большого количества ответов. — JP] Еще один подход, которым я рекомендую пользоваться при удалении файлов, состоит в создании нового сценария или псевдонима команды. Этому сценарию можно дать имя del Удаление фатов 347
23.07 или, скажем, Rm. Тогда при вызове команды, если на самом деле ее не существует, не будет причинено никакого вреда — вы просто получите сообщение об ошибке. Освоив такой подход, вы сможете делать сценарии удаления своих, файлов более безопасными. В качестве примера приведем один из таких сценариев, который спрашивает о каждом из файлов, если их всего не более трех. При наличии большего количества файлов сценарий выводит их имена и спрашивает один раз, хотите ли вы их удалить: #!/bin/sh case $# in 0) echo "'basename $0": you didn't say which file(s) to delete"; exit 1;; [123]) /bin/rm -i "$@" ;; *) echo "$*" echo do you want to delete these files\? read a case "$a" in [yY]*) /bin/rm "$@" ;; esac - BB 23.07 Более безопасный способ удаления файлов в некоторых каталогах Использование переменной noclobber озм) и файлов, защищенных от записи, предупреждает возникновение лишь некоторых случайных ошибок. Потенциально катастрофическая ошибка возможна при наборе % rm * .0 вместо' % rm *.о Все ваши файлы пропадут в мгновение ока. Простая и эффективная мера защиты — создание файла с именем -/' в том каталоге, который нужно защитить дополнительно: touch 21.07 % touch ./-i .1-23.14 В предыдущем случае метасимвол * заменяется именами всех файлов в каталоге. Поскольку файл -I, согласно алфавиту <51.оз), находится впереди всех файлов (кроме тех, имена которых начинаются символом !, #, $, %, &, \ (),*, + или ,), то команда rm воспринимает -/как аргумент командной строки. При выполнении команды rm с опцией -/ (2i.ii) файлы не удаляются без вашего одобрения. И все же здесь не все в порядке. Если в каталоге есть файл, имя которого начинается с запятой (,), то оно будет идти впереди имени, начинающегося с дефиса, в результате чего команда rm не получит с самого начала аргумент -/. Файл -/ не предостережет вас и от следующей ошибки: % rm [a-z]* .0 [Два замечания относительно столь удобного классического приема Брюса. Во-первых, если все пользователи создадут в каждом из своих сотен каталогов файлы с именем -/, то это приведет к расходованию большого количества индексных дескрипторов (1.22). Возможно, лучше было бы создать один файл -/ в своем начальном каталоге и жесткие ссылки (24.04) для остальных каталогов: % cd % touch ./-i % cd каталог - 14.11 % in ~/-i . 348 Часть третья. Работа с файловой системен
23.09 Во-вторых, для экономии дисковых блоков сделайте так, чтобы файл -/' был нулевого размера. Для этой цели используйте команду touch, а не vi или другую команду, которая помещает символы даже в пустой файл. — JP] - ВВ 23.08 Безопасное удаление: за и против Чтобы предупредить последствия случайного удаления файлов, некоторые пользователи создают где-нибудь каталог trash (мусорная корзина), а затем пишут программу безопасного удаления. В соответствии с этой программой файлы не удаляются совсем, а перемещаются в указанный каталог. Реализация такой программы может быть довольно сложной, но и простой псевдоним с именем del может делать большую часть того, что требуется: alias del "rav \!* -/trash" Конечно, теперь ваши удаляемые файлы накапливаются в каталоге trash, поэтому его время от времени нужно чистить. Это можно делать либо вручную, либо автоматически: &&44.09 23 2 * * * cd $HOME/trash && rm -rf * -г 23.17 Такая запись в файле crontab (40.12) задает ежесуточное удаление всего содержимого каталога trash в 2:23 после полуночи. Чтобы восстановить удаленный файл, а затем вернуть его в нужное место, каталог trash придется просмотреть вручную. Это может оказаться ненамного приятнее, чем рыться в мусорной корзине в поисках выброшенного по ошибке чека, но будем надеяться, что вы нечасто будете допускать ошибки. С описанным подходом связано множество проблем. Очевидно, если вы удалите в один день два файла с одинаковыми именами, то один из них будет утрачен. Решить эту проблему, по-видимому, сможет соответствующий сценарий, но вам придется выбирать для удаляемого файла новое имя. Здесь есть ряд побочных эффектов и неожиданностей, особенно если вы хотите иметь эквивалент команды rm -r, работать в сети рабочих станций или же когда вы применяете этот способ для удаления файлов, совместно используемых группой пользователей. К сожалению, это действительно проблема. Безопасное удаление, которое на самом деле не является таковым, наверное, не стоит затрачиваемых усилий. Безопасная сеть со множеством брешей хороша до тех пор, пока вы не попадете в одну из них. Брешь можно заделать, заменив простую команду rm сценарием. Но исправить все недостатки невозможно. Действительно надежный метод решения проблемы описан в параграфе Джонатана Кейменса (Jonathan Kamens), посвященном пакету delete (23.Щ. ' - ML 23.09 Пакет delete: защита файлов от случайного удаления С проблемой предотвращения случайного удаления файлов приходится сталкиваться довольно часто, поэтому существует множество уже реализованных и доступных решение. Какое из них следует выбрать, зависит от того, какие возможности вы хотите получить, а также от пожеланий относительно способа реализации решения. Часто пользователи отказываются от решений, связанных со сценарием, описанным выше (2з.м), поскольку они не обеспечивают приемлемой скорости работы, не надежны и, как правило, не позволяют восстанавливать достаточно давно удаленные файлы. Например, в университете Пердью работает разветвленная сеть различных машин, использующих как локальные, так и сетевые файловые системы р.зз). Их система восстановления файлов entomb заменяет определенные системные вызовы (например, open{7), unlink{7)) функциями системы entomb, которые следят за тем, разрушаются ли какие-нибудь файлы в результате выполнения запрашиваемого системного вызова. И если это так, то создается резервная копия файла (путем вызова локального или удаленного демона системы entomb), прежде чем будет выполнен сам системный вызов. УдапеНие файлов 349
23.09 Преимущество этой системы состоит в том, что никакого нового приложения для безопасного удаления файлов создавать не нужно — стандартная команда гт автоматически делает все правильно, равно как mv или любая другая команда, потенциально способная уничтожить файл. Даже после выполнения команды cat a b > а все можно восстановить. Основной же недостаток entomb — обязательное наличие исходного кода пакета для вашей UNIX-системы и возможность перекомпиляции его утилит для связывания с библиотеками entomb. Более того, если вы хотите инсталлировать эту систему на своих машинах, то нужно иметь возможность установить ее на всех машинах сети. Если кто-то поработает на машине где система entomb установлена, а затем захочет использовать ее на рабочей станции, для которой нет исходного кода, то сделать это будет невозможно. Кроме того, существует другая опасность. Люди, привыкшие к системе entomb, которая предохраняла их от совершения ошибок, могут потерять файл, если будут использовать команды гт или mv на машинах, где эта система отсутствует. Если вы не можете строго следить за всеми машинами, в которых необходимо обеспечить защиту от удаления файлов, или не имеете исходного кода и, следовательно, не можете использовать систему, подобную entomb, то существует ряд других возможностей. Одна из них — пакет delete, написанный сотрудниками Массачусеттского технологического института. Пакет delete устраняет некоторые недостатки системы entomb. Он очень прост, компилируется Ш*> 1 практически на любой машине и для проведения инсталляции не требуе"- прав доступа ^__^_^ суперпользователя. Это значит, что если вы привыкли работать с пакетом delete на одной системе, "rfe/ete " а затем перешли на другую, то можете перенести исходный код пакета и перекомпилировать его в новой системе. Более того, пакет delete намеренно не назван гт, чтобы применяющие его пользователи помнили, с чем они работают, и не привыкали к мысли, что файлы, удаленные с помощью команды гт, можно восстановить. Однако это предполагает постоянное использование пакета delete вместо команды гт, когда нужно удалять файлы. Особенность работы пакета delete состоит в переименовании файлов путем добавления префикса, помечающего их как удаленные. Например, команда delete foo просто переименовывает файл foo в .#foo. Приведем примеры выполнения команд delete, undelete, lsdel к expunge. Каталог сначала содержит три файла: % is а Ь с Один из файлов удаляется: % delete a Имя удаленного файла не выводится обычной командой Is, поскольку начинается с точки (.). Однако оно появляется, когда выводятся имена скрытых файлов или когда выполняется команда Isdel: % Is b с % Is -A .#а b с % lsdel а Восстановление файла возвращает все в исходное состояние: % undelete a % Is a b с Можно удалить все: % delete * % lsdel a b с Можно по-настоящему удалить отдельный файл из текущего каталога: % expunge a % lsdel b с % expunge -А 16.11 350 Часть третья. Работа с файловой системой
23.10 После выполнения последней команды expunge в каталоге не останется ни одного файла: % lsdel % Is -A % Подход, используемый пакетом delete, имеет как преимущества, так и недостатки. Сначала перечислим преимущества. • Пакет способен работать в файловой системе любого типа — локальной, NFS, AFS, RFS и прочих. Для его работы на файловом сервере запускать демон не нужно. Не нужен также демон, который предотвращал бы архивирование удаленных файлов. • Местоположение удаленного файла сохраняется, так что он может быть восстановлен в том же каталоге. • Сохраняются права доступа и принадлежность файлов, вследствие чего те из них, которые были удалены, восстанавливаются с прежними атрибутами. Более того, удаленный файл может быть восстановлен каждым, кто имеет право его удалять, а не только тем пользователем, который его удалил. Теперь назовем недостатки. • Удаленные файлы учитываются в дисковой квоте (24.17) пользователя до тех пор, пока они на самом деле не будут окончательно удалены (либо системой спустя несколько дней после удаления командой delete, либо пользователем с помощью команды expunge, которая входит в пакет delete). Некоторые пользователи на самом деле могут считать эту возможность преимуществом, поскольку предотвращаете* использование занимаемого удаленным файлом пространства для сохранения других файлов (что возможно в системе entomb). • Список удаленных файлов можно просмотреть с помощью команды Is -а. Эта возможность большинством пользователей рассматривается как относительно небольшой недостаток, в первую очередь из-за того, что файлы, имена которых начинаются с точки (.), считаются, как правило, скрытыми (i6.ii). • Прежде чем окончательно удалить файл посредством команды expunge, его необходимо найти в файловой системе, тогда как в системе entomb такие файлы содержатся в одном месте. Этот недостаток также относится к разряду незначительных, поскольку большинство администраторов каждую ночь и так просматривают всю файловую систему (23.27) с целью удаления определенных временных файлов. • Только программа entomb защищает файлы. Пользователь по-прежнему может уничтожить файл с помощью команд mv, cat a b > а и других. Вы можете избежать случайного удаления файлов посредством команды гт, и добиться этого не так уж сложно. Однако не совсем ясно, стоят ли дополнительные накладные расходы, необходимые для обеспечения работы системы, подобной entomb, получаемых преимуществ (даже если у вас имеются все условия для инсталляции системы entomb). Работа систем entomb и delete основана на двух наиболее распространенных подходах к проблеме защиты от случайного удаления файла. В других пакетах для решения поставленной задачи выбирается один из этих механизмов. - ПК 23.10 Осознанное удаление: команда rm -f Опция -f команды гт является полной противоположностью опции ч (21.и). Она означает: "Удалить все без лишних вопросов". Считается, что название "Г происходит от слова "force" (принуждать), но это не совсем так. Команда гт -/не "принуждает" систему удалять файлы, для работы с которыми у вас нет соответствующих полномочий. (Чтобы знать, какие файлы вам разрешено удалять, нужно разобраться с правами доступа (22.02).) Что же тогда делает команда rm -f и зачем она нужна? • Обычно команда/тя запрашивает подтверждение (Override protection 444 for foo?), если вы просите ее удалить файл, права на запись в который у вас не имеется. (Файловая система позволяет удалять файлы, доступные только для чтения, при условии, что вы Удаление файлов 351
23.11 владеете файлом и имеете право на запись в каталог.) При наличии опции -/эти файлы будут удалены без лишних вопросов. • Обычно код завершения (44.07) команды гт равен 0, если команда выполнена успешно, и -1, если файл удалить не удалось. При наличии опции -/код завершения всегда равен 0. Я заметил, что редко использую команду гт -fa командной строке, но почти всегда применяю ее в сценариях. Вряд ли кто-то захочет, чтобы выполнение его сценария прерывалось множеством вопросов в случае, когда команда гт обнаружит пакет файлов, доступных только для чтения. [Никому, вероятно, не захочется также, чтобы выполнение его сценария было прервано при попытке команды гт -/ удалить несуществующие файлы. В некоторых версиях UNIX в подобном случае сообщение об ошибке выводится, в других — нет. — JP\ - ML 23.11 Удаление файлов с необычными именами Часто возникает проблема с удалением файлов, в именах которых присутствуют необычные символы (или другие "странности"). Следующие несколько параграфов. содержат рекомендации относительно удаления: • файлов со случайными управляющими символами в именах (параграф 23.12); • файлов с "нулевым" именем (параграф 23.13); • файлов с именами, начинающимися с дефиса (параграф 23.14); • файлов с "непечатаемыми" именами (параграф 23.15); • файлов посредством использования номера индексного дескриптора (параграф 23.16); • каталогов и связанных с этим проблем (параграф 23.17). Мы также даем советы относительно удаления: • неиспользуемых (или редко используемых) файлов (параграфы 23.19 и 23.20); • всех файлов каталога, за исключением одного-двух (параграф 23.21). - ML . 23.12 Использование метасимволов для удаления файлов с необычными именами О именами файлов бывает трудно работать, если они содержат управляющие символы или специальные символы интерпретатора shell. Рассмотрим каталог, в котором есть три файла со странными именами: % is What now a$file prog| . с program.с • Если набрать эти имена файлов в командной строке, то интерпретатор shell будет интерпретировать специальные символы (пробел, знак доллара, вертикальную черту), а не включать их в имя файла. Есть несколько способов (2j.ii) решения этой проблемы. Один из них — с помощью метасимволов (is.02). Наберите часть имени файла без специальных символов и используйте метасимволы, соответствующие остальной части. Как объясняется в параграфе 8.05; интерпретатор shell не ищет специальных символов в имени файла после замены метасимволов, поэтому их безопасно использовать для сравнения. Вот как можно переименовать'файл What now ъ Whatnow, удалить файл a$file и переименовать файл prog\.c в prog.c: % mv What* Whatnow % rm -i a* rm: remove a$file? у % mv prog?.с prog.c 352 Часть третья. Работа с файловой системой
23.14 Имена файлов с управляющими символами представляюет собой другой вариант той же проблемы. Используйте метасимволы для сравнения с той частью имени, которая вас беспокоит. Настоящая проблема, связанная с управляющими символами, возникает из-за того, что некоторые из них делают странные вещи с экраном терминала. Однажды у меня появился файл с символом [CTRL-L] в имени. Когда я запускал команду Is, этот символ вызывал очистку экрана прежде, чем я успевал разглядеть имя файла! В параграфе 16.14 объясняется, что в UNIX-системах на базе BSD вместо обычной команды /5 можно использовать команду Is -д, а в System V — команду Is -b. Это должно позволить проанализировать "нестандартное" имя и сконструировать выражение с метасимволами для переименования или удаления соответствующего файла. (Команда Is -q выполняется по умолчанию во многих современных реализациях BSD UNIX. Поэтому, используя BSD, вы, скорее всего, никогда не столкнетесь с подобной проблемой.) - JP 23.13 Удаление файлов с "нулевым" именем Я писал этот параграф с тревогой и дрожью в коленках. Я никогда не пользовался командой clri, которая является "слишком сильным лекарством". Однако есть вещи, о которых рано или поздно>1ужно рассказать. Мне попалось несколько сообщений о файлах с пустыми именами — они, надо полагать, возникли вследствие неправильного взаимодействия между персональными компьютерами, использующими NFS <из), и UNIX-системами. [Мне также пришлось столкнуться с несколькими сообщениями о файлах, в имена которых входила косая черта (/). Последние приходили через сетевую файловую систему от компьютеров Macintosh. Описанный здесь метод должен помочь и в работе с такими файлами. — JP) В силу множества причин файлы с "нулевыми" именами практически невозможно удалить. Поэтому при их появлении вам нужно действовать следующим образом: • с помощью команды Is -aiLf определить номер индексного дескриптора о.22) каталога, содержащего файл с "нулевым" именем; • стать суперпользователем и демонтировать файловую систему, которая содержит каталог с файлом, имеющим "нулевое" имя; • воспользовавшись командой clri файловая_система номер_индексного_дескрмп- тора, "очистить" индексный дескриптор каталога, где аргумент номер_индексного_де- скриптора относится к каталогу, содержащему файл с "нулевым" именем, а аргумент файловая_система является именем файловой системы, содержащей указанный каталог; • запустить команду fsclc и позволить ей исправить нарушения. - ML 23.14 Работа с именем файла, начинающимся с дефиса (-) Часто по недосмотру создаются файлы с именами, начинающимися с дефиса (-), например: output, -f. Это вполне допустимое имя. Проблема в том, что опции команд UNIX также обычно начинаются с дефиса. Если вы наберете такое имя файла в командной строке, то команда может решить, что вы ввели опцию. Почти в каждом из таких случаев достаточно "спрятать" дефис от команды. В частности, имя файла можно начать с . / (точки с косой чертой), и это, можно сказать, ничего не изменит. Символы . / означают, что нужно искать в текущем каталоге" (i.2ij. А вот как можно удалить файл -/ % rm ./-f Большинство версий команды /тя для работы с именами файлов, начинающимися с дефиса, имеют специальную опцию, однако описанный прием должен работать во всех командах UNIX. - JP Удаление файлов 12 9-171 353
23.15 23.15 Использование команды unlink для удаления файлов с необычными именами В некоторых версиях UNIX возникает множество проблем с именами файлов, содержащими 8-битовые символы (не ASCII-символы (si.oj)). Команда fa ^ (Ч-W представляет не ASCII-символы вопросительными знаками (?), однако обычные команды, типа rm -i * (2з.п>, пропускают такие файлы. Рассмотреть поочередно все символы, из которых состоит имя файла, можно, по крайней мере, с помощью команды od -с (25.07), которая выведет дамп текущего каталога, если будет указано его относительное имя . (точка) (i.2i). (Отметим, что некоторые версии UNIX имеют команду fa -b (Н.н), которая делает то же самое, что и od -с, но более простым способом.) % Is -q ???? afile bfile % rm -i * afile: n bfile: n % od -c . 00 \t 360 207 005 254 \0 \0 \0 \0 ... Если вы сможете переместить все другие файлы за пределы данного каталога, то, вероятно, удалите оставшиеся файлы вместе с каталогом посредством команды rm -rf (2.117, 23.10). Перемещение файлов и удаление каталога — плохой способ избавления от файлов с нулевыми именами, если это касается такого важного системного каталога, как /bin. Можно также попытаться найти имя файла в списке, выведенном командой od (оно, скорее всего, будет заканчиваться последовательностью символов [NUL], например такой: \0 \0 \0. ..). Подобный файл можно удалить напрямую с помощью системного вызова unlink(2) в Perl-сценарии. Поставьте обратную косую черту (\) перед каждым восьмеричным значением, взятым из списка, который выводится командой od: perl -e 'unlink("\t\360\207\005\254");■ Если у вас нет интерпретатора Perl, напишите небольшую программу на С (S2.osy. % vi unlink.с % cat unlink.с main () ( unlink("\t\360\207\005\254"); } % cc unlink.с % ./a.out Команда fa сообщит вам, сработала ли ваша программа (дело в том, что если она не сработала, то сообщений об ошибках не будет). - JP 23.16 Удаление необычных файлов по номеру индексного дескриптора Если метасимволы не работают (23.12) при удалении файла со странным именем, попробуйте определить номер индексного дескриптора файла 0.22). Затем для удаления файла используйте оператор -inum (П.ю) команды find. Рассмотрим каталог, в котором есть файл с необычным именем. Команда fa (с опцией ^ (К.н), выбираемой в BSD UNIX по умолчанию) показывает, что в имени есть три необычных 354 Часть третья. Работа с файловой системой
23.17 символа. При выполнении команды Is -i выводится номер индексного дескриптора каждого файла. Странный файл имеет номер 6239. Передайте этот номер команде find, и файл исчезнет: % is adir afile b???file bfile cfile dfile % Is -i 6253 adir 6239 b???file 6249 cfile 9291 afile 6248 bfile 9245 dfile % find . -inum 623V -exec rm {} \; % Is adir afile bfile cfile dfile Файл можно было не удалять, а переименовать в newname с помощью следующей команды: % find . -Inum 6239 -exec rov {} newname \; Если текущий каталог содержит большие подкаталоги, то с целью повышения скорости работы можно добавить оператор -prune (17.23) команды find. - JP 23.17 Проблемы с удалением каталогов А как быть, если нужно удалить каталог? Стандартный и самый безопасный способ осуществления этого — использование утилиты rmdir (или ее GNU-версии, имеющейся на компакт-диске): % rmdir files Команда rmdir часто приводит в замешательство начинающих пользователей. Она удаляет каталог только при условии, что он абсолютно пуст; в противном случае выводится сообщение об ошибке: % rmdir files rmdir: files: Directory not empty % Is files % Как в данном примере, команда Is нередко показывает, что каталог пуст. Что происходит? Довольно часто редакторы и другие программы создают "скрытые" файлы (файлы с именами, начинающимися с точки). Обычно команда Is не приводит их в списке. Чтобы увидеть эти файлы, нужно воспользоваться командой fa -a (i6.ny. % rmdir files rmdir: files: Directory not empty % Is -a files .BAK.textfile2 Теперь видно, что каталог вообще-то не пуст — в нем находится файл резервной копии, оставленный каким-то редактором. Возможно, для очистки каталога вы захотите задействовать команду rm *, но она не сработает, так как игнорирует имена файлов, начинающиеся с точки, если только явно не указать, что такие файлы необходимо удалить. На самом деле нам нужен шаблон с метасимволами, например . ??* или какими-нибудь другими (is.ny. % rmdir files rmdir:■ files: Directory not empty % Is -a files .BAK.textfile2 % rm files/.??* % rmdir files % Удаление файлов 12» 355
23.18 Другим камнем преткновения могут стать файлы с именами, содержащими непечатаемые или пробельные символы, — они могут создаваться случайно или нарочно (кое-кто находит это забавным). Наличие подобных файлов обычно приводит к "подозрительному" выводу команды Is (i6.iз) (например, к появлению пустой строки). Если вас не интересуют все эти "особые" случаи, просто выполните команду гт -г. % rm -r files Эта команда удаляет каталог и все, что содержится в нем, в том числе и другие каталоги. Многие считают, что это опасно, поскольку можно удалить больше, чем ожидаешь. Лично я постоянно использую эту команду и никогда не вожусь с командой rmdtr. - ML 23.18 Как происходит создание и удаление каталогов Каждый файл в файловой системе UNIX (каталог также является просто файлом, хотя и несколько необычным) представляется одним индексным дескриптором (1.22) и одним или несколькими именами (записями каталогов (is.o2)). Если индексный дескриптор считать файлом, то каждое имя является ссылкой (ПМ) на него. Обычный файл может иметь от одной до нескольких тысяч ссылок (точное их количество зависит от системы), но каталог никогда не имеет меньше двух ссылок. Каждый каталог имеет, по крайней мере, два имени. Допустим, вы находитесь в каталоге /usr/tmp и выполняете команду mkdlr x. Какие две ссылки будут иметься на каталог х? Это ссылки /usr/tmp/x и /usr/tmp/x/. — записи в каталогах /usr/tmp и /usr/tmp/x соответственно. На первый взгляд это выглядит довольно странно: как каталог может ссылаться на самого себя? Понять это несложно: сначала создается совершенно пустой каталог /usr/tmp/x, затем — ссылка /usr/tmp/x на запись /usr/tmp/x/ Поддела сделано! Ссылка нужна только для того, чтобы первое имя превратить в номер индексного дескриптора, т.е. в сам файл, а затем создать новую запись для второго имени, указывающую на этот дескриптор. Нужно также создать ссылку из /usr/tmp на запись /usr/tmp/x/.., чтобы получить правильно сформированный каталог. И программа mkdir, и системный вызов mkdir делают все надлежащим образом, и для обычного пользователя не существует другого способа создания каталога, если только он не является суперпользователем (1.24). Вот где возникают проблемы. Все, что делает системный вызов unlink{2), — это берет предоставляемое ему имя, уменьшает значение счетчика ссылок в индексном дескрипторе, а затем удаляет имя. Если это имя было последней ссылкой на данный дескриптор, то сам файл также уничтожается. В противном случае файл остается нетронутым и продолжает быть доступным под своими другими именами. А что произойдет, если уничтожить ссылку на каталог? Если каталог совершенно пуст, то он просто пропадет. Но если в нем есть записи . и .., а они наверняка есть, то все не так просто. Ссылка . на сам каталог продолжает существовать, поэтому файл, которым является каталог, не удаляется. Имя /usr/tmp/x удалено, и нам нужно решить, как избавиться от последних записей . и ... Сделать это невозможно. Данный каталог будет существовать вечно. Более того, он содержит ссылку на каталог /usr/tmp, откуда следует, что второй каталог также невозможно удалить. Конечно, команда fsck (которая не использует стандартные механизмы файловой системы) может почистить каталоги, но для этого обычно нужно останавливать систему. [Команда fsck является командой проверки файловой системы, которую может выполнять системный администратор. — JF] По этой причине только суперпользователь может уничтожить ссылку на каталог. Обычные процессы должны использовать программу rmdir или системный вызов. Между прочим, системные вызовы mkdirQ) и rmdir{2) в старых UNIX-системах отсутствовали. Поэтому в этих системах для запуска программ mkdir и rmdir нужно было использовать последовательности вызовов fork-exec (ЗШ). — СТ, из телеконференции net.unix в Usenet, 25 июля 1986 г. 356 Часть третья. Работа с файловой системой
23.20 23.19 Удаление страниц руководства, которые не читаются (BSD) В BSD команда man (sooi) поставляется вместе с неформатированными страницами руководства, содержащимися в каталогах с именами /usr/man/man... и /usr/share/man.... Для форматированных страниц руководства есть также пустые каталоги с именами /usr/man/cat... и /usr/share/cat.... При запуске команды man для чтения страниц диалогового руководства каждая его форматированная страница помещается в эти каталоги cat, поэтому в следующий раз, когда такие страницы нужно будет прочитать, форматирование не понадобится.* Однако форматированные страницы руководства могут занимать много дискового пространства. Вы можете оставить несколько предварительно отформатированных страниц, пользующихся большим спросом, и удалить те, которые применяются редко: -atime 21.05 find /usr/man/cat? -atime +5 -exec rm -f {) \; — CT, из телеконференции comp.unix.quetions в Usenet, 19 апреля 1987 г. 2320 Удаление устаревших файлов Постепенно в ваших каталогах собирается много "хлама" — файлов, за которыми вы не следите и которые никогда не используете. Можно написать такую команду find^(n.oi), которая будет автоматически удалять подобные файлы. Если вы хотите производить очистку регулярно, добавьте команду find в свой файл crontab (Ш2). Главное, что нужно для этого сделать, — это написать команду find, которая будет находить файлы с учетом времени последнего доступа {atime (izosj), и использовать оператор -ok или -exec (17.Ы), предназначенный для удаления файлов. Команда должна иметь примерно такой вид: % find . -atime +60 -ok rm -f {} \; Эта команда находит файлы, к которым не было доступа в течение последних 60 дней, спрашивает, хотите ли вы их удалить, а затем, получив утвердительный.ответ, производит удаление. (Если команда выполняется демоном стп, не забудьте использовать оператор -exec вместо -ok и удостоверьтесь в том, что команда find не станет удалять файлы, которые вы считаете важными.) Конечно, вы можете так изменить эту команду find, чтобы она выполняла (или выбирала) файлы с конкретными именами. Приведенная ниже команда, к примеру, удаляет лишь старые файлы дампа и резервные копии файлов редактора GNU Emacs, имена которых заканчиваются на ~: % find . \( -name core -о -name "*~" \) -atime +60 -ok rm -£ {) \; Если вы выберете автоматизированный способ удаления устаревших файлов, то необходимо будет иметь в виду следующие обстоятельства. • Есть множество файлов (например, утилиты UNIX, файлы регистрации), которые никогда не должны удаляться. Поэтому не запускайте никаких сценариев автоматического удаления ни в каталогах /us); / ни в каких-либо других системных каталогах. • В некоторых системах при работе с двоичными исполняемыми файлами время последнего доступа к ним не обновляется. Поскольку читать эти файлы нет необходимости, вы можете решить, что они совершенно устарели, даже если часто используются. Но удалять их не стоит. Задав достаточно сложную команду find, вы сможете находить такие файлы автоматически. Что-то в этом роде можно сделать (хотя бы частично) с помощью следующего трюка: . ! 17.06 % find . -atime +30 ! -perm -111 ... -exec rm {} \; -peim 17.16 В новых реализациях BSD (Net.2 и 4.4BSD) применяется другая схема построения руководства. Здесь используются только файлы, предварительно отформатированные в каталогах cat*. (Неформатированные версии страниц содержатся вместе с исходным кодом и обновляются в результате внесения изменений в исходный код.) Удаление файлов 357
23.21 • Исходя из тех же соображений, вы вряд ли захотите удалять исходные тексты на С поэтому команду find нужно модифицировать, чтобы она имела такой вид: % find . -atime 430 ! -perm -111 ! -name "*.c" ... -exec rm {) \; • Лично я считаю автоматическое удаление файлов чрезвычайно экстравагантным решением. Не могу себе представить, как можно удалять файлы, не зная, что, собственно, удаляется, или, по крайней мере, не сохраняя их в "мусорной корзине'' — на тот случай, если будет удалено что-то важное. Чтобы обеспечить архивирование ая.оч) удаляемых файлов на ленте следует использовать оператор -cpio команды find (если он есть в вашей системе). Я уже подчеркнул, что не считаю сценарии автоматического удаления файлов хорошей идеей. Вы спросите, каковым в таком случае будет мое предложение? У меня нет хорошего и универсального решения. Я трачу достаточно много времени (примерно один час в месяц), просматривая каталоги и вручную удаляя устаревшие файлы. Для этой цели имеется также команда clean — правда, я часто забываю о ней. Определение команды clean имеет такой вид: alias clean "rm *~ junk *.ЕАК core #*" Это значит, что данная команда удаляет все резервные копии файлов редактора Emacs (32.ni), файлы автосохранения редактора Emacs (допускаю, что это рискованно), файлы с именем junk, некоторые другие резервные копии, а также файлы дампа (>3.oi). И поскольку я никогда не сохраняю эти файлы, то мог бы "смириться" с командой % find - \( -name "*-■' -о -name core \) -atime +1 -exec rm {} \; Но все равно автоматическое удаление меня сильно нервирует, поэтому я предпочитаю обходиться без него. - ML 23.21 Удаление всех файлов, кроме одного При работе в UNIX приходится сталкиваться с одной проблемой: система не слишком хорошо умеет делать исключения. В частности, нет такой опции команды /то, которая бы означала: "Делай все что угодно с остальными файлами, но не удаляй этот файл". Вы, как правило, можете создавать выражения с метпсинво1ами ощ, которые будут делать то, что вам нужно, но часто это требует больших затрат труда, а иногда такое выражение создать невозможно. Как раз в этой ситуации будет уместно поговорить об операторах подстановки результатов выполнения команды (\>.м) (обратные кавычки). С помощью команды Is можно вывести список всех файлов, передать вьшод по каналу команде grep -у или egrep -у (2?.оз), а затем использовать обратные кавычки для предоставления результирующего списка команде гт. Вот как примерно должна выглядеть такая команда: % rm -i 4s -d *.txt | grep -v ' Ajohn\.txt$' " [На самом деле для сравнения только одного имени файла больше подходит команда fgrep -у -х (27.06). — JP] Данная команда удаляет все файлы, имена которых заканчиваются на .txt, кроме файла john.txt. Возможно, я проявляю излишнюю осторожность, чтобы не допустить дополнительных совпадений, так как в большинстве случаев достаточно было бы команды grep -vjohn. Использование команды Is -d (П.он) предотвращает поиск заданных имен файлов в подкаталогах. Перед удалением каждого файла команда гт -I (2i.ii) запрашивает подтверждение. Если вы в себе уверены, опустите опцию -/. Конечно, если вы хотите исключить два файла из общего списка, то это можно сделать с помощью команды egrep: % rm "Is -d *.txt I egrep -v 'Ajohn|mary'" 358 Часть третья. Работа с файловой системой
23.22 (Не все реализации команды egrep поддерживают опцию -v. He забывайте заключать в кавычки вертикальную черту (|), чтобы предупредить передачу по каналу вывода команды egrep "команде" тагу.) Другим решением указанной проблемы является применение сценария пот (ls.oy. - ML 23.22 Использование команды find для удаления ненужных файлов Запускаете ли вы на своей машине команду find каждую ночь? Представляете ли вы себе, сколько работы должна выполнить эта команда, чтобы найти, скажем, файлы трехдневной давности объемом менее 10 блоков, а также принадлежащие пользователям /red или roof! Я попытался объединить выполнение необходимых для удаления файлов задач в одном большом сценарии, содержащем команду find: #! /bin/sh # # cleanup - поиск файлов, которые должны быть удалены, # и удаление их из файловой системы. 2>&1 45.21 find / \( -о -о -о -О -о -о \) -print - \( \( \( \( \( \( \( ■exec -name •#*' -name ',*' -name rogue.sav \( -name '*.bak' -o -name '*.dvi' -o -name '*.CKP' -o -name '.*.bak -o -name '.*. CKP -name '.emacs [0—9]*' -name core -user guest rm -f () \; > Vtmp/.< 1 ' \) i -atime -atime -atime -atime -atime -atime cleanup 2>S1 +1 + 1 +7 +3 +7 + 9 \) \) \) \) \) \) \) \ \ \ \ \ \ \ \ \ \ \ [Здесь приведен пример использования одной команды find для поиска файлов с разными именами и различным временем последнего доступа (см. параграф 17.05). Как заметил Крис, поиск всего этого посредством одной команды find выполняется намного быстрее и с меньшей нагрузкой на диск, чем в случае запуска множества команд find. Отдельные части выражения группируются при помощи скобок. Продуманный принцип отступов облегчает чтение длинных строк. Операторы -print и -exec в конце командной строки обеспечивают удаление каждого найденного файла, а также запись имен файлов в файл с именем /1mp/.cleanup, который потом можно прочесть, с тем чтобы узнать, какие файлы были удалены. Вы, надо полагать, понимаете, что вывод имен файлов в файл /tmp/.cleanup позволяет каждому видеть путевые имена типа /home/joe/personal/resume.bak, которые для некоторых пользователей могут иметь конфиденциальный характер. Кроме того, обратите внимание, что эта команда find начинает поиск с корнеюго каталога. Вы можете делать то же самое с собственными каталогами. — Щ — СТ, из телеконференции net.unix.wizards в Usenet, 9 июня 1985 г. Удаление файяов 359
24 Другие способы освобождения дискового пространства 24.01 Не удаляйте файл, а очистите его Иногда есть смысл не удалять файл полностью, а только сделать его пустым. • Новый файл, созданный после удаления старого и получивший его имя, будет иметь стандартные права доступа (22М), а его владельцем станете вы (22М). Целесообразно сначала сделать файл пустым, а затем добавить в него необходимый текст, причем права доступа и принадлежность не изменятся. • Совершенно пустые файлы, которые, согласно информации, выводимой командой к -/, не содержат ни одного символа, не занимают никакого дискового пространства, кроме нескольких байтов, используемых для записи файла в каталоге (is.o2). • Пустые файлы можно использовать в качестве "маркеров места" для напоминания о том, что там раньше был какой-то документ или что документ должен быть там. Некоторые UNIX-программы регистрации не будут записывать сообщения об ошибках, если файлов регистрации не существует. А пустые файлы как раз годятся для этого. • Пустые файлы сохраняют временные метки (подобно файлам, содержащим текст), которые указывают, когда в последний раз файл был модифицирован. Я использую пустые файлы в некоторых каталогах для хранения информации о датах выполнения определенных операций (создания резервных копий, распечатки (ищ и т.п.). Искать файлы по временным меткам может команда find -newer (i7.os, п.щ. Ну вот, теперь вы имеете об этом какое-то представление. Как сделать файл пустым? Заметим, что некоторые редакторы, сообщив, что в файле нет ни одной строки, могут при сохранении такого файла дописать символ новой строки. Для хранения даже одного символа требуется блок дискового пространства. Способы создания пустого файла перечислены ниже. • В интерпретаторе Bourne shell лучше всего переадресовать вывод "пустой" команды: $ > afil» Если файл уже существует, то эта команда усечет файл, не запуская порожденный интерпретатор shell. • В интерпретаторе С shell файл /dev/null (u.h) можно скопировать в новый файл: % ср /dev/null afile Можно также получить почти пустой файл, оставив в нем всего несколько строк: tail 25.14 % tail afile > tmpfile % cat tmpfile > afile % rm tmpfile 360 Часть третья. Работа с файловой системой
24.03 Последний способ особенно целесообразен при работе с файлами регистрации, которые никогда не следует удалять полностью. Используйте команды cat и гт, а не команду mv, которая уничтожит ссылку на исходный файл (afile) и заменит этот файл временным. - JP 24.02 Экономия дискового пространства путем направления файлов регистрации и почтовых сообщений на "битодробилку" Некоторые UNIX-программы (обычно это фоновые программы или демоны) записывают информацию о своих действиях в файл регистрации. Вы, возможно, не хотите, чтобы этот файл создавался, поскольку испытываете дефицит дискового пространства. Приведем в этой связи несколько советов. • Некоторые программы осуществляют запись в файл регистрации только при условии, что он уже существует. Когда программа не выполняется, попробуйте удалить файл регистрации. • Если вы удаляете файл регистрации, а программа его воссоздает, поищите а документации опцию командной строки или соответствующую установку в файле конфигурации, которые сообщат команде, что файл регистрации создавать не нужно. • В системе, поддерживающей символические ссылки (is.04>, попробуйте заменить файл регистрации ссылкой на файл /dev/null оз.ыу. # rm logfile # In -s /dev/null logfile Если повезет, программа не станет "жаловаться" и будет отправлять все свои сообщения на "битодробилку". Определите, какие программы выполняются при перезагрузке системы либо из системного файла crontab (40.12), чтобы усечь или заменить файл регистрации. Такие программы могут заменить символическую ссылку небольшим обычным файлом, который может опять увеличиться. • Если в системный почтовый ящик для пользователя uucp поступает почта о.зз>, от которой вы хотели бы избавиться, попробуйте добавить в его начальный каталог файл .forward, содержащий одну-единственную строку: /dev/null Можно также добавить в файл псевдонимов системной почтовой службы псевдоним, с помощью которого почта будет отправляться по тому же адресу: uucp: /dev/null Если в вашей системе есть команда newaliases, предназначенная для перестройки базы данных псевдонимов, не забудьте ее выполнить после внесения изменений. - JP 24.03 Удаление ссылок на открытые файлы — не лучшая идея [Некоторые программы создают временные файлы, открывают их, а затем удаляют ссылки на них (т.е. удаляют файлы), не закончив чтение (45.щ. Следовательно, другие пользователи не могут работать с этими файлами (удалять, читать или переписывать их). Поскольку файл открыт процессом, то UNIX удаляет запись в каталоге, соответствующую файлу (ссылку на него), не освобождая дискового пространства до тех пор, пока процесс не закончит работу с этим файлом. В данном параграфе объясняется, почему не следует так делать. (Между прочим, замечание Криса о том, что системные администраторы должны чистить файловую систему путем очистки открытых файлов, заслуживает внимания.) — JP] Другие способы освобождения дискового пространства 361
24.04 Чтобы представить еше один довод в пользу того, что ссылки на открытые файлы лучше не удалять (кроме всего прочего, это приводит, скажем так, к "интересным" результатам при работе с NFS (из)), рассмотрим следующую команду: multi 1000 </usr/dict/words >/tmp/filel (Команда multi делает п копий своего ввода. Здесь п равно 1000.) Теперь предположим, что размер каталога /тр (21.02) становится слишком большим. Попробуйте ввести rm /tmp/filel # а файл-то, на самом деле, не удаляется! рз ах # найти процесс "multi" kill идентификатор процесса # избавиться от него или же /dcv/null 1.1.14 ср /dev/null /tmp/filel # теперь можно поставить все на место Изменим немного пример, допустив, что объем каталога /tmp превышает заданные размеры и есть группа открытых (4s.2i>) файлов, ссылки на которые удалены. Чтобы освободить занимаемое ими пространство, нужно уничтожить процессы, которые открыли эти файлы. Однако если это обычные файлы, их просто можно сделать пустыми. Есть одна веская причина, оправдывающая удаление ссылок на открытые временные файлы: если программа, обрабатываюшая эти файлы, завершится некорректно, то временные файлы исчезнут. Другого надежного способа их удаления не существует. Нужно балансировать между преимуществами и недостатками такого способа. — СТ, из телеконференции net.unix.wizards в Usenet, 9 июня 1985 г. 24.04 Экономия дискового пространства с помощью ссыпок Вы можете держать несколько копий одного и того же файла в разных каталогах при условии, что он: • предназначен для чтения несколькими пользователями (это может быть файл данных, файл конфигурации программы, список телефонов и т.д.); • представляет собой программу, которую должны использовать несколько пользователей, а вам как одному из таких пользователей хотелось бы, чтобы у каждого была своя копия файла; • имеет нестандартное имя или находится в каталоге, которым вы обычно не пользуетесь. Вы хотите дать файлу имя, которое проще набирать, но не можете воспользоваться командой mv. Поэтому вместо команды ср вы можете воспользоваться командой In. (В вашей системе эта команда должна быть, а ее GNU-версия имеется также на компакт-диске.) Ссылки (is.03) имеют множество преимушеств. Одним из основных преимуществ жестких ссылок является то, что они не занимают дискового пространства.* Чем больше файл, тем больше пространства экономится с помошью ссылок. Символическая ссылка всегда занимает некоторое дисковое пространство, поэтому жесткая ссылка имеет преимущество, если, конечно, не направлена за пределы файловой системы — в этом случае ее использовать нельзя. Многие пользователи не применяют ссылок, так как считают, что все ссылки и файл должны иметь одинаковые имена. Но это не так. Ссылка в одном каталоге может называться myjile, а во втором — file.allan. UNIX с этим справляется. - JP 24.05 Ограничение размеров файлов Познакомимся с методикой, способной предотвратить появление файлов большого размера. Последние могут быть созданы случайно, в частности, неуправляемой программой. Чтобы установить максимальный размер файла в интерпретаторе С shell, используйте команду limit Запись ссылки занимает несколько символов в каталоге, в котором она создается. Если это не приводит к тому, что каталог занимает еше один дисковый блок, то объем свободного пространства иа диске не изменяется. 362 Часть третья. Работа с файловой системой
24.07 f ilesize максимальный_размер (обычно это делается в файле xshrc), а в интерпретаторе Korn shell — команду ulimit -f максимальный_размер. Обе команды можно задавать в командной строке. Так, приведенные ниже команды интерпретаторов csh и ksh предотвращают создание файлов размером более 2 Мб: % limit filesize 2m $ ulimit -f 2000 После выполнения такой команды UNIX не позволяет выделять дополнительное дисковое пространство, поэтому размер файла не превышает 2 Мб. В Berkeley-системах для ограничения размеров файла дампа (S3.oi) можно использовать команды limit и ulimit. Файлы дампа обычно имеют большой размер и могут быть созданы по такой "безобидной" причине, как неправильный вызов команды. Чтобы задать максимальный размер файлов дампа, выполните одну из следующих команд: % limit coredumpsize максимальный_ра.эмер $ ulimit -с максима льный_ра.эмер Чтобы вообще запретить создание файлов дампа, используйте нулевое значение параметра максимальный_размер. Поскольку файлы дампа очень важны для эффективной отладки, пользователи, которые активно занимаются этим, должны знать команды, снимающие ограничение на размер: unlimit coredumpsize — в интерпретаторе csh и ulimit -с unlimited — в интерпретаторах bash и ksh. — ML, из книги System Performance Tuning издательства O'Reilly & Associates 24.06 Экономия дискового пространства с помощью знаков табуляции Утилита £zi£ (24.07), а также другие утилиты для сжатия файлов, и в частности compress, уменьшают объем файла путем сжатия текста. Прочитать такой файл без декомпрессии невозможно. Если файл содержит, скажем, текст со множеством пробелов или текст в виде столбцов, между которыми много свободного пространства, либо если строки набраны с отступами, то уменьшить размер файла, сохранив его читабельность, несложно. Это делается путем замены пробелов символами [TAB] с помощью команды unexpand. Если ее нет в вашей системе, обратитесь к компакт-диску. ^_^_ Как работает эта программа? В параграфе 41.04 рассказано о работе с символами табуляции в Y "^ UNIX. Каждый символ [TAB] может заменить сразу 8 символов пробела. По умолчанию команда I Ф I unexpand заменяет пробелы символами [TAB] только со стороны левого поля. При указании ^*ш.4 опции -а заменяются все пробелы, где только это возможно (конечно, без изменения положения unexpand столбцов текста). Путем сравнения списков, выводимых для данного файла командой Is -I до и после выполнения команды unexpand, можно определить, сколько дискового пространства удалось сэкономить. Причем этот показатель может изменяться от файла к файлу. При выполнении указанной операции необходимо учитывать следующее. • На вашем терминале могут отсутствовать фиксированные позиции табуляции (через каждые 8 символов), поэтому для отображения файла, обработанного программой unexpand, используйте команду cat. Проверьте, имеет ли файл такой же вид, как и до обработки. • При пересылке файла, обработанного командой unexpand, по сети другому пользователю могут возникнуть проблемы. Не исключено, что вам понадобится предварительно заменить символы табуляции с помощью команды expand (4i.04>. • Редактирование файла с символами табуляции может оказаться непростым делом. Перед редактированием файлу лучше вернуть первоначальный вид (expand), а сжать его (unexpand) можно после редактирования. - JP 24.07 Сжатие файлов с целью экономии дискового пространства Большинство файлов можно сжимать, в результате чего они будут занимать меньше места. Допустим, у нас есть текстовый файл. Каждая буква занимает один байт (что обеспечивает возможность работы с 256 символами), но почти все символы файла относятся к Другие способы освобождения дискового пространства 363
24.08 алфавитно-цифровым или служат знаками препинания, а подобных символов всего около 70. К тому же чаще используются строчные буквы, буква "е" встречается чаще, чем, скажем, "z", причем она нередко бывает удвоенной и т.д. С учетом сказанного можно предположить, что для хранения каждого символа не нужно иметь целый байт. Таким образом, можно написать алгоритм, позволяющий уменьшить размер файла на 50% и более. Алгоритмы сжатия — сложная тема, которую здесь мы обсуждать не будем. Да вам и не нужно о них что-либо знать. Во многих UNIX-системах для сжатия файлов имеется прекрасная утилита compress. Правда, алгоритм ее работы, кажется, защищен патентами, поэтому многие пользователи ее избегают. Более новой и даже лучшей утилитой, к тому же не имеющей проблем с патентованием, является GNU gzip. Тот, кто ее не имеет, может обратиться к компакт-диску. ^^^_ Чтобы сжать файл, введите команду | <"•) J % gzip *айл ^!—»^ Имя файла меняется на файл.gz. Опция -v заставляет команду gzip сообщать, сколько глр дискового пространства сэкономлено. Обычно этот показатель составляет 40-60%. Если файл не следует сжимать из-за наличия жестких ссылок (is.04) на него или если сжатый файл с таким именем уже существует, то утилита gzip выведет соответствующее сообщение. Чтобы "вынудить" команду gzip в такой ситуации все-таки сжать файл, попробуйте воспользоваться опцией -/ Она более уместна при использовании команды gzip в сценариях, когда вы не хотите задумываться, можно ли сжимать тот или иной файл. Сжатые файлы всегда являются двоичными, и даже если они получены из текстовых файлов, читать их невозможно. Чтобы вернуться к исходному файлу, воспользуйтесь утилитой gunzip; % gunzip файл (Утилита gunzip обрабатывает также файлы, полученные с помощью утилиты compress. Для этой же цели можно использовать утилиту uncompress.) В конце имени файла суффикс .gz часто опускается. Если нужно только прочесть файл, не восстанавливая его исходную версию, примените команду gzcat, которая просто декодирует файл и направляет его в стандартный вывод. Особенно удобно передавать по каналу вывод команды gzcat команде more (25.<в) или grep (27.01). (Для файлов, сжатых с помощью утилиты compress, существует команда zcat, хотя с ними может работать и команда gzcat.) На компакт-диске имеется несколько сценариев, которые автоматически распаковывают сжатые файлы, а затем упаковывают их. Речь идет о сценариях для редактирования (zvi, zex и zed (24.il)), просмотра (zmore, zless и zpg (2S.os)) или запуска любой команды, которая может читать данные из канала (zloop (24./»>). Существует и ряд других утилит для сжатия, используемых в сообществе. UNIX. Впрочем, утилита gzip работает во многих операционных системах. Она надежна и распространяется бесплатно. Поэтому большинство пользователей выбирают именно эту утилиту. - ML, JP 24.08 Экономия дискового пространства: команда tar и сжатие дерева каталогов В файловой системе UNIX файлы сохраняются в дисковых блоках (S3.oi). Каждый непустой файл, независимо от своего размера, занимает по крайней мере один блок.* Каталоги, содержащие множество небольших файлов, могут занимать большое количество частично пустых дисковых блоков. Большой файл более выгоден, поскольку он полностью занимает все (за исключением, может быть, последнего) блоки. Команда tar (1я.о5) может читать множество файлов малых размеров и помещать их в один большой файл. Впоследствии, когда один из таких файлов вам понадобится, восстановить Полностью пустой файл, не содержащий ни одного символа, не занимает ни одного блока. 364 Часть третья. Работа с файловой системой
24.08 его можно будет из tor-архива. Неплохая идея для экономии дискового пространства, не так ли? Однако команда tar, которая была разработана в первую очередь для создания ленточных архивов, добавляет в конец каждого файла лишние символы, чтобы сделать число, определяющее размер файла, четным. Поэтому большой fcr-архив занимает примерно столько же блоков, сколько отдельные файлы малых размеров. В таком случае для чего же я написал настоящий параграф? Дело в том, что указанную проблему может разрешить утилита gzip (24.07). Она сжимает файлы, избавляясь прежде, всего от повторяющихся символов. В результате этого объем tor-архива обычно уменьшается на 50% и более. Создание сжатого архива каталога и всех его подкаталогов не представляет труда: команда tar копирует все дерево, когда ей предоставляется имя самого верхнего каталога. Убедитесь только, что tor-архив создается в том каталоге, который не будет сам архивироваться, — иначе команда tar будет пытаться архивировать собственный архив! Обычно я помещаю архив в родительский каталог. Например, чтобы заархивировать каталог с именем project, мне нужно было бы воспользоваться командами, приведенными ниже. Если вы работаете в системе, где длина имени файла ограничивается 14 символами, проследите, чтобы имя архивного файла (в данном случае project.tar.gz) не оказалось слишком длинным. Расширение имени .tar.gz не является обязательным и добавляется только по соглашению. Внимательно следите за сообщениями об ошибках: % cd project ..1.21 % tax elf - . I gzip > . . /project.tar.gz % cd .. -r 23.17 % rm -r project Команда tar l обеспечивает вывод сообщений, если на какой-нибудь из архивируемых файлов имеется другая жесткая ссылка (is.04). Если на многие файлы имеется по нескольку ссылок, то архивирование каталога сэкономит не очень много дискового пространства, так как файлы все равно останутся на диске даже после выполнения команды гт -г. При необходимости получить список файлов, хранящихся в архиве, используйте команду tar t или tar tv: more25.07 % gzcat project, tar.gz | tar tvf - | more rw-r—r—239/100 485 Oct 5 19:03 1991 ./Imakefile rw-rw-r--239/100 4703 Oct 5 21:17 1991 ./scalefonts.c rw-rw-r—239/100 3358 Oct 5 21:55 1991 ./xcms.c rw-rw-r—239/100 12385 Oct 5 22:07 1991 ./io/input.c rw-rw-r—239/100 7048 Oct 5 21:59 1991 ./io/output. с Для извлечения файла из архива введите команду % mkdir project % cd project % gzcat ../project.tar.gz I tar xf - Конечно, совсем необязательно извлекать файлы в каталог project. Вы можете копировать архивы из других каталогов, перемещать их на другой компьютер и т.д. Из архива можно также извлекать лишь некоторые файлы. При этом должны использоваться те же имена, какие были выведены командой tar t выше. Например, чтобы восстановить старый подкаталог project/io (а также все его содержимое), нужно ввести команду % mkdir project % cd project % gzcat ../project.tar.gz I tar xf - ./io - JP Другие способы освобождения дискового пространства 365
24.09 24.09 Определение доступного дискового пространства Два инструментальных средства, команды dfa du, сообщают, сколько дискового пространства свободно и сколько используется указанным каталогом. Команда <#" определяет для каждой файловой системы процент заполнения — сколько пространства используется и сколько свободно. По умолчанию эта команда выводит данные как для локальных, так и для удаленных (доступных с помощью NFS(i.3jj) файловых систем. В BSD UNIX вывод команды <#" выглядит следующим образом: % df Filesystem /dev/diskOa /dev/disk3d /dev/disk5e /dev/disk2d /dev/diskle toy:/usr toy:/ Данный отчет представляет информацию о пяти локальных файловых системах и двух удаленных (из системы toy). Файловые системы /research и /field почти полностью заполнены (98% и 97% соответственно), в то время как остальные системы все еще имеют много свободного пространства. Возможно, вы захотите предпринять какие-то меры по освобождению дисковой памяти в двух упомянутых файловых системах. Заметим, что файловая система BSD, заполненная на 100%, на самом деле на 10% свободна, но эти 10% дискового пространства может использовать только суперпользователь (1.24) (а это не всегда хорошо). Команду df можно вызвать и другими способами. • Если вы уже знаете, какая файловая система вас интересует, то можете использовать команду df /homes или df. (точка означает "текущий каталог" a.2i)). • При условии, что ваша система использует сетевые файловые системы, а вас интересуют только локальные, применяйте команду df-t 4.2. Эту команду нужно задействовать всегда, когда удаленный сервер выключен. Если в вашей системе смонтированы диски удаленных машин, которые в данный момент не доступны, то команда df будет выполняться очень медленно. • Если вас интересует степень использования индексных дескрипторов (1.22), а не заполненность файловой системы, используйте команду df -/. Она выводит примерно такой же отчет, представляющий статистику по индексным дескрипторам. При работе в файловой системе старой System V отчет команды <#" выглядит иначе. Однако предоставляемая информация, по сути, будет такой же. Вот отчет, типичный для XENIX- системы: % df / (/dev/root ): 1758 blocks 3165 i-nodes /u (/dev/u ): 108 blocks 13475 i-nodes /us (/dev/us ): 15694 blocks 8810 i-nodes HB корневой файловой системе имеется 1758 физических блоков (содержащих всегда по 512 байтов, независимо от размера логического блока файловой системы) и 3165 индексных дескрипторов. Для определения полной емкости файловой системы используйте команду df-t. df Команда df -I выводит данные только о локальных файловых системах, опуская файловые системы, смонтированные с помощью NFS или RFS. Команда dfspace (в System V.3 и V.4) выводит отчет, имеющий более привлекательный внешний вид и по стилю похожий на отчет команды df в BSD. Для каждой файловой системы команда dfspace показывает объем свободной дисковой памяти как в килобайтах, так и в процентах от размера самой файловой системы. Вы можете попробовать задействовать и записанную на нашем компакт-диске GNU-версию команды df kbytes 889924 505463 635287 505463 956094 498295 7495 used 724308 376854 553121 444714 623534 341419 5883 avail 76620 78062 18637 10202 236950 107046 862 capacity 90% 83% 97% 98% 72% 76% 87% Mounted on / /benchmarks /field /research /home /usr /root 366 Часть третья. Работа с файловой системой
24.10 Часто бывает полезно знать, сколько дискового пространства занимает тот или иной каталог. Это позволяет, в частности, определить, не занимает ли кто-то из пользователей больше места на диске, чем ему было выделено. Такой отчет предоставляет утилита du. Вот его обычный вид: % du 107 ./reports 888 ./stuff 32 ./howard/private 33 ./howard/work 8 68 ./howard 258 ./project/code 7 69 ./project 2634 Этот отчет показывает, что текущий каталог и все его подкаталоги вместе занимают примерно 2,5 Мб (точнее, 2634 Кб). Самыми большими каталогами в этой группе являются sniff и howard, имеющие объем 888 и 868 Кб соответственно. В отчете также указывается объем дисковой памяти, занимаемый подкаталогами (/howard/work и т.п.). Команда du не представляет отдельные файлы как обособленные элементы, если только не вызвана с опцией -а. Заметим, что в отчете, выводимом в System V, единицей измерения считается блок размером в 512 байтов, а не килобайт. Опция s заставляет команду du выводить общий объем дискового пространства, занимаемый каталогом; вывод отдельных строк для подкаталогов подавляется. Например: % du -s 2 634 Это как раз последняя строка предыдущего отчета. — ML, из книги System Performance Tuning издательства O'Reilly & Associates 24.10 Сценарий zloop: обработка сжатых файлов Преимущество использования сжатых файлов (24.оТ) заключается в возможности экономии дискового пространства. Их недостаток проявляется тогда, когда сжатых файлов много и ко всем из них нужно обращаться отдельно, набирая каждый раз команду gzcat (или icai). Это надоедает и отнимает много времени. Я написал сценарий с именем zloop, принимающий в качестве аргумента имя команды, которую нужно выполнить, и список сжатых файлов. Сценарий запускает команду gzcat отдельно для каждого файла и передает вывод по каналу указанной вами команде. Поскольку команда gzcat понимает также файлы формата compress, то она может обрабатывать и файлы с расширением Z. Сценарий отображает выполняемую им командную строку и вывод команды (если таковой имеется). Если команда возвращает ненулевой код (44.П7), то сценарий выводит предупреждение. % Is 185.gz 187.gz 189.gz 191.gz 193.gz 195.gz 197.gz 186.gz 188.gz 190.gz 192.gz 194.gz 196.gz 198.gz % zloop 'egrep "ASubject:.*group"' *.gz ===== zloop: zcat 185.gz | egrep ""Subject: . *group" == Subject: List of Active Newsgroups ===== zloop: zcat 186.gz | egrep ""Subject: . *group" === Subject: Alternative Newsgroup Hierarchies ===== zloop: zcat 187.gz | egrep ""Subject:. *group" === zloop: note: that command returned 1 (non zero) status: 'usr/local/bin/gzcat 187.gz I egrep ""Subject:.*group"' Другие способы освобождения дискового пространства 367
24.11 ==== zloop: zcat 187.gz | egrep "^Subject:.*group" === Subject: Checkgroups message (with INET groups) Subject: Checkgroups message (without INET groups) Subject: Monthly checkgroups posting - - Сценарий zloop слишком многословен для UNIX-команд, но его можно сделать более "молчаливым", выполнив небольшой объем редактирования. Сообщения о состоянии направляются в стандартный поток ошибок. Поэтому, если вы хотите направить вывод сценария zloop, скажем, программе постраничного вывода тоге, сообщите интерпретатору shell, что нужно объединить стандартные потоки вывода и ошибок: |& 13.05 % zloop 'egrep "ASubject:.*group"' *.gz |& more для csh 2>&l 45.21 $ zloop 'egrep "ASubject:.*group"' *.gz 2>Ы | more для sh При использовании канала (|) сообщения о состоянии и вывод команды могут смешаться (им). Объясним подробнее: если переадресовать вывод сценария zloop, то будут переадресованы потоки вывода всех команд, которые он запускает. Это значит, что ввести команду tr3j.ll % zloop "tr "[A-Z]" "[a-z]" I pr -2' *.gz > toprint -2 3S. 17 — все равно, что вручную ввести команды ( 13.07 ( gzcat файл1.gz | tr "[A-Z]" "[a-z]" | pr -2 gzcat файл2.gz | tr "[A-Z]" "[a-z]" | pr -2 gzcat файлЗ.gz I tr "[A-Z]" "[a-z]" I pr--2 ) > toprint <9) ] и направить стандартный вывод интерпретатора shell оз.т), а также всех этих команд в файл toprint. Сценарий zloop имеется на компакт-диске. - JP zloop 24.11 Редактирование сжатых файлов Использование сжатых файлов w.oy позволяет экономить дисковое пространство. Но работать с ними не очень удобно: чтобы получить возможность читать или редактировать, эти файлы необходимо развернуть. Сценарий zvi облегчает редактирование. Он распаковывает файлы, сжатые с помощью утилиты GNU gzip, а затем запускает текстовый редактор vi, ех или ed. (Сценарий нетрудно модифицировать, что даст возможность использовать другие редакторы.) Редакторы vi и ех могут редактировать по нескольку файлов (зо.щ, и сценарий справляется с этим. После редактирования всех файлов сценарий опять их сжимает в. фоновом режиме, поэтому окончания его работы ждать не нужно. Здесь использован еще один интересный прием: вместо того чтобы распаковать все указанные вами файлы до запуска редактора, сценарий распаковывает только первый файл, а остальное делается в фоновом режиме, пока вы редактируете первый файл. (Сценарий сначала определяет, как будут называться все распакованные файлы. К тому времени, когда редактор до них доберется, они наверняка будут распакованы.) Подобный прием позволяет экономить дисковое пространство и за счет того, что большую часть времени файлы находятся в сжатом виде. Начать работу можно почти так же быстро, как и при редактировании несжатых файлов, особенно если первый файл небольшой. Приведем пример. Отредактируем файлы"qlog.gz и /usr/central/data.gz при помощи редактора w. Затем запустим команду zed с файлом bigfile.gz % zvi qlog.gz /usr/central/data вводить ".gz" необязательно ...Редактирование двух файлов, как в обычном многофайловом сеансе редактора vi zvi: re-gzipping qlog /usr/central/data in the background... ■ % zed bigfile < edscr 173351 размеры отредактированных файлов 368 Часть третья. Работа с файловой системой
24.12 183079 zed: re-gzipping bigfile in the background... v>^h При возникновении каких-либо ошибок программа сообщает о них, если может; в противном | (ф) | случае бы получите сообщение электронной почты о.зз) с копией сообщений об ошибках. ^._*Ц Прежде чем использовать этот сценарий в своей системе, пожалуйста, тщательно проверьте zvi его. Все "хитрые" приемы нуждаются в небольшой доводке. Большая часть сценария довольно очевидна. К сожалению, он не будет работать с редактором Emacs (З2.01), пытающимся открыть все файлы сразу. Но этот недостаток можно устранить, если заставить сценарий распаковать все файлы до запуска редактора Emacs. Думаю, вам будет интересен следующий абзац. Он касается распаковки файлов в фоновом режиме. Каким образом сценарий обнаруживает ошибку, возникшую в фоновом процессе? test -n "$bgfiles" && $uncompress $bgfiles >$t 2>sl S $prog $ files if [ -s $t ] then echo "$myname: 'gunzip $bgfiles' bombed:" 1>&2 4 cat $t 1>£2 $echo "Should I try to gzip all files [ny](n)? $nnl" read ans Стандартные потоки вывода и ошибок фонового задания направляются во временный файл $t, затем запускается редактор ($prog). После выхода из редактора временный файл проверяется с помощью опции s условной конструкции ([) (44.20). Если файл не пуст, то сценарий показьгаает содержащиеся в нем сообщения об ошибках и спрашивает у вас, нужно ли файлы опять сжимать. Сценарий написан таким образом, что должен иметь еще две ссылки (п.оз). Но вы можете создать больше или меньше ссылок — их количество зависит от того, сколько редакторов имеется в вашей системе. При инсталляции сценария с компакт-диска ссылки будут созданы автоматически. Если же вы сами набираете текст сценария, поместите его в доступный для выполнения файл с именем zvi, а затем создайте ссылки: % chmod 755 zvi % In zvi zex % In zvi zed Сценарий, проверив на основании значения переменной $0 имя, по которому он был вызван, решает, какой редактор использовать. Этот прием позволяет экономить дисковое пространство: Вы можете заменить применяемые редакторы путем редактирования сценария, а также путем добавления или удаления ссылок. Полные путевые имена используемых в сценарии программ могут быть изменены в вашей системе. - JP 24.12 Сжатие дерева каталогов: точная настройка Приведем быструю команду, способную выполнять сжатие (24.07) файлов текущего каталога и его подкаталогов. В ней используется команда find (I7.Q2), предназначенная для рекурсивного поиска и отбора файлов, которые должны быть сжаты: .-size 17.14 % find . ! -perm -0100 -size +1 -type f -print | xargs gzip -v xar^ 9.21 Эта команда находит все файлы, которые: • не являются доступными для выполнения (! -perm -0100; поэтому мы не сжимаем сценарии и другие программные файлы); • занимают больше одного блока, поскольку при сжатии файла, занимающего один блок и меньше, экономии дискового пространства не получится. Однако в вашей файловой системе выражение -size +1 может не соответствовать файлам длиной в один блок. Не исключено, что вам придется использовать выражение -size +2, -size +1024c или какое-либо другое; • являются обычными файлами (-type f), а не каталогами, именованными каналами и т.д. Другие способы освобождения дискового пространства 369
24.13 » i Опция -v команды gzip обеспечивает вывод имен файлов и коэффициентов сжатия Используйте ее, если в вашей системе нет команды xargsr. % find . ! -perm -0100 -size +1 -type f -exec gzip -v {} \; Настройте эту команду таким образом, чтобы она выполняла нужные вам операции. Приведем некоторые идеи. (За более подробной информацией обращайтесь к страницам системного руководства по команде find.) ! -name \*.gz Пропускать уже сжатые файлы (файлы с именами, заканчивающимися на .gz) -links 1 Сжимать только файлы, не имеющие других (жестких) ссылок -user ваше_имя Сжимать только файлы, принадлежащие вам -atime +60 Сжимать только файлы, к которым не было доступа в течение 60 дней и более Возможно, вы захотите использовать подобные приемы в задании, выполняющемся каждый месяц или с другим интервалом с помощью команд at (4о.оз) или сгоп (40.пу. - JP Экономия дискового пространства, занимаемого исполняемыми файлами: команда strip После компиляции (S2.os) и отладки программы в исполняемом двоичном файле остается информация, которую в целях экономии дискового пространства можно удалить. Это должна сделать команда strip. Обратите внимание, что после обработки файла командой strip невозможно будет использовать с ним такой символический отладчик, как dbxi Приведем пример. Скомпилируем программу на языке С и выведем ее размер с помощью команды Is. Затем усечем файл посредством команды strip и опять выведем его размер. Объем сэкономленного дискового пространства зависит от нескольких факторов, но сэкономить удается почти всегда: % ее -о echoerr echoerr.c -s 17.14 * Is -Is echoerr 52 -rwxr-xr-x 1 jerry 24706 Nov 18:49 echerr % strip echoerr % Is -Is echoerr 36 -rwxr-xr-x 1 jerry 16656 Nov 18:49 echerr Если вы заранее знаете, что во время компиляции файл будет обрабатываться посредством команды strip, используйте компилятор се с его опцией -s. В случае применения команды Id, скажем в файле makefile (2S.U), можно воспользоваться ее опцией -s. Сценарий stripper, предназначенный для быстрого освобождения дискового пространства, находит все неусеченные исполняемые файлы в вашем каталоге bin (4.62) и усекает их. (Этот же сценарий, просматривающий всю файловую систему, может сэкономить для системного администратора еще больше дискового пространства, однако нужно избегать применения необычных имен файлов (9.22)): #! /!bin/sh skipug="! -perm -4000 ! -perm -2000" # Пропуск файлов с установленными битами # SETUID и SETGID find $HOME/bin -type f \( -perm -0100 $skipug \) -print | xargs file | sed -n '/executable . *not stripped/s/:[TABl. *//p' I xargs -t strip Команда find (17.02) ищет все файлы, для которых не установлены биты SUID и SGID (U.H), и запускает команду file (2s.ot), чтобы получить описание каждого файла. Команда sed 370 Часть третья. Работа с файловой системой
24.15 пропускает сценарии и прочие файлы, усечь которые невозможно. Эта команда ищет в выводе команды find строки типа /usr/local/bin/xemacs:|TAB|xxx. . ■ executable ххх. . . not stripped со словом executable (исполняемый), а затем — строки со словами not stripped (неусеченный). Редактор serf удаляет двоеточие, знак табуляции, описание, после чего передает имя файла команде strip. - JP 24.14 Проявляйте осторожность при использовании команды strip Одним из интересных способов экономии дискового пространства является обработка непосредственно исполняемых (двоичных) файлов с помощью команды strip (24.13). Возможно, вы подумываете о ее рекурсивном запуске посредством команды find (п.ю). Будьте осторожны! В некоторых операционных системах применение команды strip к файлу с установленным битом SUID (1.23) приводит к удалению этого бита, в результате чего программа перестает быть SUID-программой. Целесообразнее заставить команду find пропускать файлы с указанными битами. Например, приведенная ниже команда ищет все доступные для выполнения файлы (17.15) без установленных битов SUID и SGID (п.щ: % find . -type f -perm -0001 ! -perm -4000 ! -perm -2000 .. . Кроме того, не усекаются файлы ядра (например, /vmunix), а также файлы, которые должны быть отлажены программистами. - JP 24.15 Упорядочение каталогов В параграфе 4.07 мы говорили о том, что целесообразнее создавать небольшие каталоги. При работе с большими каталогами может создаться впечатление, что ваша система стала функционировать медленнее, так как поиск каждого файла будет производиться дольше. Что значит "небольшой" каталог? Оптимальный размер каталога — не более 60 файлов. Однако здесь возникает одна проблема: размер каталога может только возрастать. Дело в том, что создание новых файлов приводит к увеличению каталога, а вот удаление файлов само по себе не делает каталог меньшим. Я не говорю, что каталог обязательно увеличивается при создании нового файла. Это не так: если файл удален, то в каталоге появляется пустая запись, которую UNIX может использовать повторно. Но факт остается фактом: каталоги могут только увеличиваться и никогда не уменьшаются — их можно только удалить. [Некоторые BSD-системы "сжимают" каталоги до меньших размеров. — JP] Итак, допустим, что у вас в силу каких-то причин образовался ряд каталогов, содержащих по нескольку сотен файлов, многие из которых — просто "мусор" (мне не раз приходилось сталкиваться с этим). Вы, конечно, можете удалить ненужные файлы, "осчастливив" таким образом свой диск, но это не уменьшит размеры каталогов. Что же в таком случае делать? Один из возможных выходов из такой ситуации описан ниже. 1. Переименуйте старый каталог: % mv project project.toobig 2. Создайте новый каталог с именем старого каталога: % mkdir project 3. Переместите из старого каталога в новый только нужные файлы: % mv project.toobig/*.txt project 4. Весь "мусор" остался в старом каталоге. Удалите его вместе с содержимым: % rm project.toobig - ML Другие способы освобождения дискового пространства 371
24.16 24.16 Упорядочение гигантского каталога Некоторые реализации быстрых файловых систем BSD никогда не позволяют сокращать каталоги. Когда файл удаляется, файловая система помечает его запись в каталоге как "недействительную", но не удаляет ее на самом деле. Старая запись может быть повторно использована при создании нового файла, но она никогда не исчезнет. Следовательно, сами каталоги со временем могут только разрастаться. По идее же они должны занимать небольшой объем дискового пространства. Поиск в огромных каталогах существенно замедляется, поэтому вам необходимо всячески препятствовать их увеличению. Во многих UNIX-системах единственным способом уменьшения размера каталога является перемещение всех нужных файлов в другое место с последующим удалением первоначального каталога. Например: Is -lgd old Определение владельца, группы и прав доступа mkdir new; chown пользователь new; chgrp группа new; chmode код new ["A-/-"?] MM mv old/.??* old/.["A—/-A?] old/* pew А и Л? - это [CTRL-aJ и DEL rmdir old mv new old Этот метод также работает в файловых системах типа V7. Но он не применим к корневой файловой системе. Но все-таки реализации быстрых файловых систем BSD, которые способны сокращать каталога, существуют. Они делают это после того, как полный просмотр каталога покажет, что некоторые из его последних фрагментов являются пустыми. Полный просмотр инициируется любой операцией, помещающей в каталог новую запись, в том числе системными вызовами creat(2) и link(2). Причем новые имена по возможности помещаются в самые первые пустые ячейки. Следовательно, в таких системах существует другой способ сокращения каталогов. [Как узнать, сокращает ли ваша BSD-система каталоги? Попробуйте выполнить приведенный ниже псевдокод (используя настоящие команды) и посмотрите, есть ли эффект. — ML] while (каталог1, который должен быть сокращен) { mv (файл, указанный в списке последним) (короткое имя) mv (короткое имя) (первоначальное имя) ) Этот прием работает как в корневом каталоге файловой системы, так и в подкаталогах. Ни один из методов не может быть применен, если какой-либо внешний агент (например, демон) занят просмотром каталога. Первый метод также не сработает, если внешний агент является неактивным, но должен возобновить работу и поэтому удерживает существующий каталог открытым. Примером такого внешнего агента может служить программа-демон sendmail, которая просматривает каталог, но в данный момент остановлена или неактивна. Второй метод требует знания безопасного короткого имени, т.е. имени, которое бы не дублировало другого имени в каталоге. Я обнаружил, что второй метод достаточно удобен для написания сценария, способного выполнять поставленные задачи. Я назвал этот сценарий squozer. #! bin/sh # # squoze last- Is -ldg IPS-' i : 45.09 while : do ,25.15 set ' Is -f | tail -10r' for i do case "$i" in "$last"|.|..) break 2;; esac 372 Часть третья. Работа с файловой системой
24.18 # _ (знак подчеркивания) является "коротким,.безопасным" именем /bin/mv -i "$i" _ ss /bin/mv _ "$i" done last="$i" done Is -ldg [Команда Is -/выводит перечень записей в том порядке, в каком они находились в каталоге, не сортируя их. — JP\ Приведенный выше сценарий не обрабатывает файлы с именами, содержащими символ новой строки. Однако он вполне безопасен при работе с файлами, находящимися в очереди почтовой утилиты sendmail (при условии, что последняя не активна). — СТ, из телеконференции net.unix.wizards в Usenet, 22 августа 1991 г. 24.17 Дисковые квоты Независимо от того, сколько у вас имеется дискового пространства, постепенно оно исчерпается. Одним из способов, посредством которого системный администратор может стимулировать очистку пользователями своих каталогов, является использование дисковых квот, BSD. UNIX поддерживает систему дисковых квот, которая "навязывает" пользователям дисковые квоты, устанавливаемые системным администратором. До появления System V версии 4 системы дисковых квот не существовало. Теперь данная ОС поддерживает дисковые квоты для файловых систем типа BSD (UFS). Как квоты влияют на вашу работу в качестве, пользователя? Рано или поздно вы обнаружите, что превысили квоту, выделяемую для каждой файловой системы. Она.может быть наложена на объем дискового пространства (количество блоков) и на количество индексных дескрипторов (количество файлов). В системе квот используются понятия жестких и мягких лимитов. Превысив мягкий лимит, вы получаете предупреждение (WARNING: disk'quota exceeded), но можете продолжать наращивать объем используемого дискового пространства. Предупреждение будет повторяться при каждом входе в систему. В какой-то момент (т.е. после определенного числа сеансов, в течение которых количество используемого дискового пространства остается выше заданного мягкого лимита) у системы "исчерпывается терпение" и она отказывается выделять дополнительное пространство. В таком случае вы получите предупреждение OVER DISK QUOTA: NO MORE DISK SPACE и должны будете удалить ряд файлов, чтобы снова оказаться в рамках мягкого лимита. Пользователям никогда не разрешается превышать жесткий лимит. Этот режим позволяет создавать большие временные файлы, но при условии, что они занимают дисковое пространство ненадолго. Квота может выделяться и на количество файлов (т.е. индексных дескрипторов (1.22)), которыми вы владеете, в каждой файловой системе. Проявляется она примерно так же: в случае превышения мягкого лимита вы получаете предупреждение, и если часть файлов не будет удалена, то система через какое-то время откажется создавать новые файлы. - ML 24.18 Большие файлы не всегда занимают много дискового пространства Просматривая содержимое файловой системы с помощью команды Is —l, можно встретить файл объемом, скажем, в 10 Мб. "Ничего себе! — скажете вы. — Он, должно быть, забирает много дискового пространства!" Но если этот файл будет удален, то команда d[(24.m) покажет, что объем свободного дискового пространства практически не изменился. Почему? Возможно, этот файл является разреженным, т.е. содержит преимущественно символы [NUL], — такое предположение, как правило, бывает верным. Команда Is -Is (п.щ показывает разреженные файлы так, что количество использованных блоков в первом столбце относительно невелико по сравнению с количеством символов: % Is -is total 128 Л21.11 &.&. 44.09 Другие способы освобождения дискового пространства 373
64 -rw-r—г-- 1 jerry 8413616 Nov 9 16:49 core 64 -rw-r—r— 1 jerry 64Ь21 Nov 7 18:22 dns.tar Программы, использующие пакет подпрограмм dbm {database management — управление базами данных), часто создают разреженные файлы, потому что указанный пакет применяет информацию о расположении файлов при создании хэш-таблиц и может разместить записи в файле базы данных таким образом, что между ними окажется много пустого пространства. Многие файловые системы UNIX (но не все; к ним, в частности, не относится Andrew File System) имеют возможность существенно уменьшить объем пространства, занимаемого файлом, который преимущественно содержит символы [NUL]. Блоки символов [NUL] на самом деле просто не записываются. Вместо этого ОС отслеживает количество таких блоков и предоставляет нужное число символов [NUL] любой программе, читающей данный файл, несмотря на то, что эти символы вовсе не были считаны с диска. Вы можете создать разреженный файл с помощью языка С, использовав системный вызов /ореи(З) для открытия файла, а системный вызов fseek(3) — для перемещения указателя намного дальше конца файла, ничего при этом не записывая. От начала и до позиции указателя, установленного системным вызовом /seek, файл будет содержать символы [NUL], а ядро, скорее всего, не станет записывать все эти символы на диск. Между прочим, при копировании разреженных файлов могут возникнуть проблемы. Ядро недостаточно разумно, чтобы обнаружить, что вы предоставляете ему разреженный файл. Следовательно, стандартные программы копирования файлов, в частности ср, которые просто читают файл из одного места и записывают его в другое, не сработают. Все закончится созданием файла, который физически занимает столько дискового пространства, сколько имеется символов [NUL] в копируемом файловом объекте. В результате у вас могут появиться реальные проблемы с дисковым пространством. [Некоторые операционные системы имеют опцию ср -z, позволяющую решить указанную проблему. — ТС\ - Л К, JP
Часть четвертая Содержимое файлов Просто удивительно, как много можно узнать о файле, даже не открывая его. Однако создание катгьтогов, упорядочение и поиск файлов — это только прелюдия к их использованию. В основном работа ведется с содержимым файлов. Глава 25 посвящена различным способам вывода содержимого файла на экран — целиком или по частям. В главе 26 содержится важная информация по более тонкой обработке содержимого файлов: описаны регулярные выражения, которые позволяют сравнивать текстовые шаблоны, а не отдельные слова или фразы. В главе 27 представлено семейство программ grep, которые используют регулярные выражения для поиска и вывода отдельных совпадающих строк в одном или нескольких файлах. Пользователи, не знающие UNIX, часто посмеиваются над командой grep из-за ее несколько странного имени, а она, можно сказать, является одним из самых удобных инструментов, предоставляемых данной системой. В главе 28 рассказано, как можно сравнить два файла, которые имеют много общего, как выделить различия и как собрать нечто цельное после того, как в разные версии будут внесены многочисленные изменения. Глава 29 посвящена некоторым специальным способам обработки файлов: проверке правописания, подсчету количества слов, а также различным методам элементарной проверки текста. - TOR
25 Просмотр содержимого файла 25.01 Коротко о содержании В этой главе говорится о разных способах вывода содержимого файла на экран. Большинство пользователей знакомы лишь с "грубым силовым подходом" команды cat (2S.02) к выполнению этой задачи, но существуют и другие инструментальные средства: • программы постраничного вывода more (25.оз) и less ps.M), обеспечивающие большую степень управляемости процессом просмотра длинных файлов; • программы просмотра сжатых и прочих нечитабельных файлов (параграф 25.05); • программы определения типа данных, содержащихся в файле (параграф 25.08); • программы добавления и удаления пустых строк или других пробельных символов перед выводом содержимого файла (параграфы 25.09—25.13); • программы просмотра начальной или конечной части файла (параграфы 25.14—25.20); • программы, выполняющие нумерацию строк (параграф 25.21). - TOR 25.02 Как поймать кошку: четыре способа Команда cat наверняка может быть первой командой, которую запомнят пользователи-новички, — в первую очередь благодаря ее странному имени (cat — кошка). На самом деле, название cat произошло от слова "concatenate", или, как иногда говорят, "catenate". Оба варианта слова означают одно и то же: "последовательно соединять; соединять цепочкой". Команда cat поочередно выводит содержимое всех файлов, имена которых переданы ей как аргументы. Команда cat имеет много применений, четыре основных из которых описаны ниже. Зачастую эти примеры не столько иллюстрируют команду cat, сколько объясняют механизм переадресации вывода (U.01), предоставляемый интерпретатором shell. 1. Первая форма % cat файл % cat файл! файл2 фавлЗ... предназначена для вывода на экран содержимого одного или нескольких файлов. После заполнения экрана вывод не прекращается. Поэтому, если содержимое файлов не умещается в одну экранную страницу, вывод "со свистом" пронесется перед глазами, и вы даже не сможете прочитать его.* Для чтения вывода с разбивкой на экранные страницы Вы можете считать эту форму команды бесполезной. И действительно, в таком простейшем виде она редко используется. Чаще эта форма команды cat применяется с некоторыми опциями форматирования вывода; иногда вывод команды направляется по каналу (l.iw другой Команде. Просмотр содержимого файла 377
25.03 используется команда more (25.оз> или какая-нибудь программа постраничного вывода например less ps.M). 2. Вторая форма % cat файлы > новий_файл используется при необходимости объединить несколько файлов в один большой. Убедитесь, что файл назначения еще не создан, иначе его содержимое будет заменено новым. Например, команда % cat chapl chap2 chap3 > book создает новый файл book, который включает в себя содержимое трех файлов. Три исходных файла продолжают существовать. 3. Третья форма % cat файл » сущвствующий_файл % cat файлы » сущвствужмций_файл предназначена для добавления содержимого одного или нескольких файлов в конец существующего файла: % cat notel note2 > note_list % cat note3 » note_liSt 4. Четвертая форма % cat > новый_файл позволяет быстро создать новый файл. В частности, ее удобно применять, когда вы еще не умеете пользоваться каким-либо стандартным текстовым редактором. При задании этой команды все, что вы набираете на клавиатуре, поступает в новый файл. (Вернуться к предыдущей строке будет невозможно.) Чтобы завершить ввод, наберите комбинацию клавиш [CTRL-d] прямо во вводимой строке. Все, о чем мы рассказываем, рассчитано, скорее, на начинающих пользователей. В параграфах 13.13, 25.07, 25.10 и 25.11 приведен ряд дополнительных советов относительно опций команды cat. - DG 25.03 Использование утилиты more для постраничного просмотра файлов Утилита more значительно мощнее утилиты cat (25.02). Она позволяет просматривать файлы постранично. Располагая только командой cat, вам при чтении длинных файлов пришлось бы либо читать очень быстро, либо иметь ловкие пальцы, чтобы успеть ввести [CTRL-s] (41.02) для остановки текста, либо удовлетвориться чтением нескольких последних строк текста. На практике даже самые неуклюжие версии утилиты more имеют огромные преимущества перед командой cat в плане чтения файлов, настолько значительные, что мне не понятно, почему многие для указанной цели все-таки используют команду cat. [Я просматриваю небольшие файлы с помощью команды cat потому, что мои утилиты more и less (25.04) установлены на очистку экрана (посредством опции -с) перед выводом файла. — ТС\ Утилита more при запуске выводит на экран лишь помещающуюся на нем часть файла. Чтобы перейти на следующую страницу, нажмите клавишу пробела. При необходимости продолжить перемещение вперед нажимайте клавишу пробела или клавишу [ENTER], которая обеспечивает переход вперед на одну строку. Более новые версии утилиты more позволяют возвращаться на предыдущие страницы посредством нажатия клавиши Ь. Перечислим некоторые особенности утилиты more. • Если в командной строке задано несколько файлов, то для перехода к следующему файлу можно набрать :п, а для перехода к предыдущему — :р. • В любое время можно вызвать подсказку, набрав h. • Набрав v, можно перейти из утилиты more в редактор W. 378 Часть четвертая. Содержимое файлов
25.04 • Поиск строки можно вести, если набрать /, а затем — нужную строку. Искомая строка может быть полным регулярным выражением (2ыт. Следующий экземпляр строки можно найти, набрав п. Если в качестве аргументов указаны имена двух или более файлов, утилита тоге в начале каждого файла будет отображать краткий заголовок. Это небольшое дополнение на удивление удобно. Предположим, нам нужно вывести все файлы с расширением М одной командой и мы хотим иметь перед каждым файлом какую-то метку. Для создания таких меток w.w можно использовать утилиту тоге: 1>г4?.«7 % more *.h I рг | 1рг 1рг*Г. DJ (Если вывод утилиты тоге направлен не на экран, зиачит, она не ожидает ввода каких-либо команд) Утилита тоге, к сожалению, имеет и ряд недостатков. • Даже лучшие версии утилиты тоге не могут возвращаться назад при чтении из канала, поэтому если данные другой команды передаются утилите по каналу, то команда b работать не будет. • Достигнув конца последнего файла, утилита тоге завершает свою работу. Это, конечно, не ошибка, но мне такое ее "поведение" не нравится. Хотелось бы иметь возможность вернуться назад или перейти к предыдущему файлу. • Некоторые реализации утилиты more (System V) завершают свою работу, если не могут найти искомую строку. Все перечисленные проблемы решает программа less. [Некоторые версии System V вместо утилиты тоге поддерживают ее аналог — программу pg. Кроме того, отдельные пользователи любят просматривать файлы с помощью предназначенной только для чтения версии view редактора vi. Я же предпочитаю команды тоге и less. — TOR] - ML 25.04 Программа постраничного вывода less v-^н Программа less является классической программой просмотра файлов, как и программа list в I 'в1 1 DOS, которая может делать так много, что трудно сообразить, зачем все это нужно. Данная L ~" J программа выполняет свою работу намного лучше, чем утилита more (2s.o.v, поэтому она стала lft[~ одной из первых программ, которые я перенес на новый компьютер. Если бы я попытался описать все возможности этой программы в настоящем параграфе, то он бы занял не менее десяти страниц. К счастью, команда less поставляется вместе со справочным файлом (вывод по команде h) и подробной документацией, которая сохраняется на диске вместе с программой. Перечислим некоторые возможности и преимущества программы less. • Ее не нужно изучать — программа less делает то же самое, что и такие известные программы просмотра файлов, как more, v[ (З0.02) и Emacs (32.01). • При чтении из канала (1.оз) программа less может обновлять экран и читать предыдущие страницы. • Имеется множество способов перемещения на определенный участок файла: по номеру строки, по заданному в процентах расстоянию от начала файла, по количеству экранных страниц, с помощью команд поиска. Кроме того, можно помечать строки и возвращаться к ним. • Для программистов: команда less работает с нестандартными позициями табуляции (4Ш), находит парные фигурные и обычные скобки. • Имеется бесчисленное множество установок и опций для управления экраном, позволяющих получить такой его вид, какой вам нравится, в том числе после операции поиска. Строку приглашения можно настроить так, чтобы отражалась вся необходимая информация. Программа lesskey (включена в дистрибутив less на компакт-диске) позволяет переопределять назначения клавиш. Свои любимые установки можно сохранять в переменной среды (s.oi) LESS. — JP, TOR Просмотр содержимого файла 379
25.05 25.05 Постраничный просмотр сжатых файлов, а также файлов, содержащих непечатаемые символы ^^^Ш Использование сжатых файлов (24.07) позволяет экономить дисковое пространство. Однако Г (9> 1 работать с такими файлами не совсем удобно: перед чтением или редактированием их Е^а^-4 необходимо распаковывать. Сценарий zmore (называемый также zpg и zless) упрощает работу. zmore Он распаковывает один или несколько файлов и передает их одной из программ постраничного просмотра — more (25.0З), pg или less (iSM). (Сценарий использует команду g^cat которая распознает как формат gzip, так и формат compress.) Сценарий может также обеспечить просмотр файлов с непечатаемыми символами посредством команды cat -v -/ -е (2s.ee, 25.07). Это безопасный способ постраничного просмотра файлов, содержащих непечатаемые символы, которые способны нарушить работу терминала. Наконец, этот сценарий позволит просмотреть последнюю версию файла системы RCS (жн). Приведем пример. Просмотрим постранично файлы data.gz и ../summary.gz, сжатые командой gzip. Затем прочтем файл с непонятным именем Ех034912, чтобы узнать, что в нем хранится: % zmore data ../summary Набирать . gz необязательно ...Первая страница распакованного файла data.gz... —More— ...Остальная часть распакованного файла data.gz... zmore: Press RETURN to see next file, '../summary1: ...Первая страница распакованного файла ../summary.gz... % vmore EX034912 ...Первая страница распакованного файла Ех034912, отфильтрованного с помощью команды cat -t -v -e... Все эти операции делает один и тот же сценарий. Написан он таким образом, что на него должно быть восемь различных ссылок (п.озу. zpg, zless, vmore, vpg, vless, rcsmore, rcspgvi rcsless. Сценарий проверяет имя, по которому был вызван, в соответствии со значением переменной 0$, чтобы решить, какую команду (gzcat, cat -t -v -e или со -р) использовать или же какую запустить программу постраничного вывода. Вы можете заменить заданные программы постраничного вывода путем модификаций сценария и добавления или удаления ссылок на него. Полные путевые имена в начале сценария для вашей системы могут быть изменены. . Большинство программ постраничного вывода не могут свободно возвращаться назад и произвольно перемещаться по файлу, когда они читают данные из канала. (Программа less это делает.) Для указанной цели обычно используется временный файл. Правда, это не настолько эффективно, как применение канала, но все-таки выход. Чтобы получить такую возможность, добавьте следующие две строки после второго оператора case: /tmp..SS2J.0J temp=/tmp/$myname$$ Шр 44.12 trap ' rm -f $temp; exit1 0 1 2 15 Кроме того, вам необходимо заменить строку в цикле while (ее номер — примерно 38) *) $cat "$1" I -$prog $opbs ; -r тремя следующими строками: *) $cat "$1" > $temp $prog $opts $ temp - JP 25.06 Что стоит за пробелами Команда cat -v (25.07) выводит непечатаемые символы в виде обычных символов. Для отображения пробельного символа в строке команда cat имеет две опции. Если в сочетании с опцией -v используется опция--/,-то символ [TAB] представляется как А1. Опция -е в 380 Часть четвертая. Содержимое файлов
2S.07 сочетании с -v обеспечивает тгометку каждого конца строки символом $. Некоторые версии команды cat не требуют наличия опции -v с двумя упомянутыми выше. Давайте сравним однострочные файлы, выведенные без опций -(-еис ними (опции должны быть набраны раздельно, между прочим; -te не дало бы результата): % cat afile This is a one-line file — boring, eh? % cat -v -t -e afile ThiS^Hs is"Ia one-line file'I— boring, eh? $ Хотя этого не видно из вывода простой команды cat, в файле присутствуют символ Backspace ([CTRL-h]) — перед первым s, два символа табуляции, каждый из которых занимает по одному пробелу, и семь символов пробела в конце строки. Знание этого может помочь вам в отладке печати и выводе на экран данного файла. Это полезно знать и программистам, которые должны анализировать или сортировать вывод других программ. - JP 25.07 Отображение непечатаемых символов с помощью команд cat -v и od -с Файлы иногда содержат символы, которые терминал не может отобразить, особенно если он работает только с ASCII-кодами. Использование некоторых символов может вызвать зависание коммуникационных программ или оборудования, исказить вид экрана и т.д. Поэтому при необходимости просмотреть файл с неизвестным содержимым простую команду cat лучше не использовать! Попробуйте для указанной цели применить команду cat -v. Она превращает непечатаемые символы в комбинацию обычных символов. Еще одной утилитой, отображающей непечатаемые символы, является od. Обычно я использую ее с опцией -с, если мне нужно просмотреть файл в символьном виде. Рассмотрим.файл, который почти наверняка содержит непечатаемые символы, а именно файл каталога. Этот пример взят из стандартной файловой системы типа V7. (К сожалению, некоторые версии UNIX не позволяют читать файлы каталогов. Если вы хотите повторить этот пример в подобной системе, воспользуйтесь сжатым файлом (24.07) или исполняемой программой из каталога /bin.) Файл каталога обычно содержит длинные строки, поэтому вывод команды cat рекомендуется пропустить через программу fold (43.щ: -Г24.16 % Is -fa comp % cat -v . | fold -62 М-Л?ЛЫ.Л@ Л@Л@Л0Л@Л@Л@Л@Л@Л@А@Л@Л@>ЛС .л@л@л@'-@л@'-@л@л@'-@л@л@'-@ M-a сотрл@л@л@л@л@л@л@л@л@л@л@л@Ма35АуеГоос1лел@л@л@л@ЫзЬл@л@л % od -с 0000000 377 016 . \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000020 > 007 . . \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000040 341 \п с о т р \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000060 \0 \0 М a s s A v e F о о d \0 \0 \0 0000100 \0 \0 h i s t \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000120 Длина каждой записи в каталоге типа V7 составляет 16 байтов. Команда od -с выводит в начале каждой строки восьмеричное число, показывающее смещение от начала файла, в байтах. Первая строка имеет смещение 0. Следующая строка начинается с байта 20 (т.е. с байта 16 в десятичной системе, в которой мы привыкли считать) и т.д. К команде od мы вернемся чуть позже. А пока рассмотрим вывод команды cat -v. Просмотр содержимого файла 381
25.07 • Вам, возможно, уже встречались такие последовательности символов, как Лы и Лс Это управляющие символы. Найдите их, пожалуйста, в выводе команды cat -v. Еще одна последовательность, Л@, представляет символ NTJL (ASCII-код О). В файле каталога он встречается очень часто; более подробно о нем рассказано ниже. Символ DEL (восьмеричный ASCII-код 177) представлен как Л?. Проверьте это по таблице ASCII-кодов (51.03). • Команда cat -v использует собственное обозначение для представления символов за пределами диапазона ASCII-кодов, т.е. 8-битовых символов. Команда cat -v выводит их как М- с последующим другим символом. В представленном примере есть два таких символа: м-Л? и м-а. Чтобы получить код этих символов, нужно прибавить восьмеричное 200. "Что-что?" — спросите вы. Посмотрим сначала на М-а. Восьмеричный код буквы а равен 141. Когда команда cat -v выводит м-а, то это означает, что код этого символа равен 341 (141+200). Точно также можно определить код символа, который команда cat выводит как М-Л?. Символ DEL, восьмеричный код которого равен 177, обозначен как Л?. При сложении чисел 200 и 177 получается восьмеричное 377. • Если символ не представляется как м-символ или ^символ, то это обычный печатаемый символ. Все записи в каталоге (., . ., comp, MassAveFood и hist) состоят из обычных ASCII-символов. Поинтересовавшись, где находятся записи MassAveFood и hist в списке команды Is, вы получите ответ: "Их там нет". Эти записи были удалены из каталога. Перед именем удаленного файла UNIX помещает два символа NUL (ASCII-код О, или Л@). Команд cat имеет две опции, -/ и -е, для отображения пробельных символов, содержащихся в строке. Опция -v без этих двух опций не обеспечивает преобразования символов TAB и завершающих символов пробела в видимую форму (см. параграф 25.06). Теперь рассмотрим вывод команды od -с. Объяснить его легче, чем вывод команды cat -v. • В выводе команды od -с некоторым символам предшествует символ обратной косой черты (\). При этом используются, если это возможно, стандартные сокращения UNIX и языка С, предназначенные для представления управляющих символов (зз.оо. Например, \п соответствует символу новой строки, \t — символу табуляции и т.д. Символ новой строки присутствует в начале записи comp; найдите ее в выводе команды od -с. Наличие в данной записи символа новой строки объясняет, почему вывод команды cat -v в этом месте начинается с новой строки. Символ NUL (ASCII-код 0) обозначен как \0. Он используется для дополнения записи, если имя содержит менее 14 символов. • Команда od -с показывает другие символы в виде трехразрядного восьмеричного кода. Например, 007 обозначает "символ, имеющий восьмеричный код 7". Команда cat -уэтот же символ показывает как AG ([CTRL-g]). Символы, имеющие восьмеричный код 200 и больше, отображаются командой cat -v как м-символы. В случае команды od -с мы увидим их восьмеричный код, например 341. Каждая запись в каталоге файловой системы UNIX версии 7 начинается двухбайтовым указателем на ее местоположение в таблице индексных дескрипторов. Когда вводится имя файла, UNIX использует этот указатель для поиска на диске информации, хранящейся в данном файле. Запись, соответствующая данному каталогу (с именем .), имеет указатель 377 016, родительский каталог (с именем ..) — указатель > 007, а запись comp — указатель 341 \п. Найдите эти записи в выводе команд cat -v. Если хотите, сравните эти выводы. • Как и команда cat -v, команда od -с всегда показывает обычные печатаемые символы в неизменном виде. Программа strings (27.19) находит в практически непечатаемом файле (таком, как двоичный исполняемый файл) печатаемые строки (например, имена файлов). - JP 382 Часть четвертая. Содержимое файлов
25.08 25.08 Определение типа файла В типичной UNIX-системе существует много различных типов файлов: базы данных, исполняемые файлы, обычные текстовые файлы, файлы различных специальных редакторов (например, Interleaf), tor-архивы, почтовые сообщения, каталоги, файлы шрифтов и т.д. Часто бывает нужным проверить, имеет ли файл надлежащий тип. Предположим, нам нужно прочесть файл с именем tar. Прежде чем набирать more tar, неплохо было бы проверить, содержит данный файл набор ваших заметок, скажем, о добыче угля или же это исполняемый файл tar. Если вы ошибетесь, то последствия могу оказаться неприятными. В частности, направление исполняемого файла tar на экран может привести к искажению установок терминала (42.04), выходу из системы и т.п. Утилита file сообщает, к какому типу принадлежит тот или иной файл.* Ее работу можно не объяснять: % file /bin/sh /bin/sh: spare demand paged executable % file 2650 2650: (nt)roff, tbl, or eqn input text % file 0001,v 0001,v: ascii text % file foo.sh foo.sh shell commands Утилита file на самом деле является весьма разумной [хотя и не все ее версии работают одинаково хорошо — JP[. Она не просто сообщает, двоичный это файл или текстовый, а просматривает начало файла и пытается определить, для чего он предназначен. Так, например, вы заметили, что файл 2650— это входной файл для утилиты nroff (43.13>, а файл foo.sh является сценарием. Команда не настолько разумна, чтобы определить, что файл 0001,v служит RCS-архнвом (2ч.щ, однако она обнаружила, что это простой текстовый ASCII-файл (si.03>. System V и SunOS позволяют настроить команду file так, чтобы она распознавала дополнительные типы файлов. Указания о том, как это делать, содержатся в файле /etc/magic. Эта команда может многое (а должна быть способной еще на большее), но мы ограничимся вводным ее описанием. Нашей целью будет обучение команды file распознаванию RCS-архивов. Файл /etc/magic содержит четыре поля: смещение тип_данных значение тип_файла Назначение каждого из этих полей описано ниже. смещение Расстояние от начала файла, начиная с которого команда file будет осуществлять поиск информации о типе файла. Если искать нужно с самого начала файла, то смещение будет равным 0. (Чаще всего так и бывает.) тип_данных Вид выполняемой проверки. Для сравнения текста используется значение string, байтов — значение byte, с двухбайтовым числом — значение short, с четырехбайтовым — значение long. значение Значение, которое нужно найти. При сравнении строк подойдет любая строка текста, в частности можно использовать стандартную для UNIX Escape-последовательность (например, \п для символа новой строки). Для числовых сравнений (byte, short, long) это поле должно содержать числовое выражение константы в стиле языка С (например, для шестна- • дцатеричного числа 77 таковым будет 0x77). тип_файла Строка, которую команда file будет выводить, если проверка окажется успешной. Итак, мы знаем, что RCS-архивы начинаются со слова head. Это слово находится в самом начале файла (смещение равно 0). И конечно, нам нужно осуществить сравнение строк. Поэтому внесем в файл /etc/magic следующее дополнение: О string head RCS archive Другое решение подобной проблемы предоставляет программа findlext (is.n). Просмотр содержимого фата 383
25.09 Это означает: "Считать файл RCS-архивом, если будет обнаружена строка head в позиции со сдвигом 0 от начала файла". Будет ли это работать? % file RCS/0002,v RCS/0002,v: RCS archive Для распознавания простых текстовых файлов приведенных сведений вполне достаточно. Но определение типа двоичных файлов, как я уже говорил, является более сложной задачей. - ML 25.09 Цобавпение и удаление пробельных символов Существует много способов изменения количества пробельных символов (символов пробела и табуляции) в строке. • В Berkeley-системах имеется команда cat -s ps.io), предназначенная для замены последовательностей из двух или нескольких пустых строк одной пустой строкой. Если у вас нет этой команды или же вам нужна какая-то другая, обращайтесь к параграфу 34.18. • Сценарий crush (25. U) удаляет все пустые строки. • Сценарии doublespace и triplespace (25.12) используются для удваивания и утраивания количества пустых строк в тексте. • Сценарий pushin (2s.U) заменяет несколько пробельных символов одним пробелом. С его помощью можно укорачивать длинные строки. • Редактор sed можно использовать перед распечаткой файла для задания отступов строк (43.09). Сценарий offset (35.07) делает то же самое более простым способом. • Для решения многих других задач вам, возможно, подойдут утилиты awk (.u.ii) и sed (.4.24). Но прежде чем их использовать, вы должны научиться их программировать. - JP 25.10 Сжатие пустых строк Для чтения вывода, содержащего большое количество пустых строк, может понадобиться много экранного пространства. Например, некоторые версии команды man в System V показывают все пустые строки при выводе страниц руководства. Чтобы это предотвратить, прочтите файл или передайте его по каналу команде cat -s. (Многие версии утилиты more (25.03) имеют подобную опцию -s.) Опция -s обеспечивает замену многих пустых строк одной такой строкой. Если ваша команда cat не имеет опции -s, подумайте о возможности использования ее альтернативы — команды sed (см. конец параграфа). Иногда кажется, что команда cat -s не работает. Проблема обычно возникает тогда, когда "пустая" строка содержит символы [SPACE], [TAB] или [CTRL-m]. Решить эту проблему можно, позволив команде sed очищать строки, начинающиеся с пробельных символов: % sed ' s/A flSPACEIIfABllCTRJ^vllCTKL-mll *$//' файл 1 cat -s В редакторе w (3i.m> и во многих драйверах терминала (42.oi) символ [CTRL-v] отменяет действие следующего за ним символа [CTRL-m] ([RETURN]) — вот почему последний символ не завершает текущую строку. Если у вас нет команды cat -s, обе задачи сможет выполнить редактор sed: % sed -e ' s/A llSPACEl.fTABllCTRL-vllCTRli^ *$// ■ -е '/•/,/*$/!<*' файл - JP 384 Часть четвертая. Содержимое файлов
25.12 25.11 Сценарий crush — аналог команды cat н crush Иногда приходится иметь дело с файлами, содержащими много пустых строк. В некоторых системах есть опция -s команды cat, которая производит сжатие соседних пустых строк в одну. Если такая опция отсутствует, можете воспользоваться сценарием crush. Этот сценарий пропускает пустые строки, а также строки, содержащие только символы пробела и табуляции. Вот его текст: ♦!/bin/sed -f /"[lSpace|[T5b]1*$/d Здесь квадратные скобки ([ ]) содержат символ табуляции и символ пробела. Этот сценарий даже не использует интерпретатор shell, поэтому он очень эффективен. Ядро непосредственно запускает редактор sed (45.оз) и передает ему сценарий в качестве входного файла, ожидаемого при наличии опции -/ Если ваша версия UNIX не выполняет напрямую команды с помощью оператора #!, введите такую версию сценария: exec sed ' /Л RSPACEIlTABU *$/d' ${l+"$@"} Он запускает интерпретатор shell, а затем команда exec заменяет интерпретатор shell редактором sed (4SM7). Использование конструкции ${1+"$@"} позволяет решить проблему работы с аргументами (46.07) в некоторых версиях интерпретатора Bourne shell. - JP 25.12 Пустые строки: удваивание, утраивание... Приведем два сценария, наиболее удобных для распечатки черновиков файлов. Они удваивают или утраивают количество пустых строк в файлах или в стандартном потоке ввода. Например: % doublespace afile | lp % prog | triplespace | lp Вот как эти сценарии выглядят: doublespace triplespace J t #!/bin/sed -f #!/bin/sed -f doublespace, G G О triplespace G Нет-нет, это не опечатка. Просто оба сценария используют команду С! (34.24) редактора sed. Эта команда добавляет символ новой строки и содержимое буфера редактора sed, который в данном сценарии пуст. В результате к каждому символу новой строки добавляется еще один. Две команды G добавляют два символа новой строки. Эти сценарии также не используют интерпретатор shell, поэтому они очень эффективны. Ядро непосредственно запускает редактор sed (4S.03) и передает ему сам сценарий в качестве входного файла, ожидаемого при наличии опции -/ Если ваша версия UNIX не выполняет напрямую команды с помощью оператора #!, используйте следующие версии сценариев: doublespace triplespace exec /bin/sed G ${l+"$@"} exec /bin/sed 'G;G' ${l+"$@"} Они запускают интерпретатор shell, а затем команда exec заменяет интерпретатор shell редактором sed (45.07). Выражение ${1 + "$@") позволяет решить проблему работы с аргументами (46.07) в некоторых версиях интерпретатора Bourne shell. Теперь вы знаете, как создаются сценарии, способные в четыре, пять и более раз увеличить количество пустых строк в файле. :-) - JP Просмотр содержимого файла 385 13 9-171
25.13 25.13 Сценарий pushin: удаление лишних пробелов При просмотре и распечатке файлов, содержащих неудобные для чтения длинные строки, рекомендуем воспользоваться программой fold (4зщ, которая выполняет перенос строк. Или же, если в каждой строке есть много пробельных символов (т.е. много набранных подряд символов пробела и табуляции), можно использовать сценарий pushin, приведенный в конце параграфа. Этот сценарий заменяет последовательности символов пробела и (или) табуляции одним пробелом, сжимая строку настолько, насколько это возможно. Сценарий читает стандартный ввод и направляет обработанный текст на стандартный вывод. Приведем в качестве примера несколько строк файла, которые не являются слишком длинными (в книге мы все равно не смогли бы напечатать очень длинные строки), но содержат много пробельных символов. Представьте себе, как мог бы вас выручить сценарий pushin, если бы строки были еще длиннее: % cat data resistor 349-4991-02 23 capacitor 385-2981-49 16 diode 405-3951-58 8 % pushin data resistor 349-4991-02 23 capacitor 385-2981-49 16 diode 405-3951-58 8 Вот текст сценария: |^^ #!/bin/sed -f l(|ll s/IISPACEIl ГГТаВП */ /g ^f*^ Внутри каждой пары квадратных скобок команда подстановки редактора sed получает символ s пробела и табуляции. Заменяющая строка содержит один пробел. И этот сценарий не использует интерпретатор shell. Ядро непосредственно запускает редактор sed (4S.03) и передает ему сценарий в качестве входного файла, ожидаемого при наличии опции -/ Если ваша версия UNIX не выполняет напрямую команды с помощью оператора #!, попробуйте воспользоваться другой версией сценария: exec sed ' s/ f|SPACE| |ТАВЦ fISPACEl |ТАВЦ */ /g ' $(l+"$@"} Он запускает интерпретатор shell, а затем команда exec заменяет этот интерпретатор редактором sed (45.07). Конструкция ${ 1+"$@" ) позволяет решить проблемы, связанные с обработкой аргументов (4Ш) в некоторых версиях интерпретатора Bourne shell. - JP 25.14 Как просмотреть конец файла: команда tail Часто бывает необходимым просмотреть конец длинного файла. Предположим, вы только что послали почту с помощью утилиты UUCP р.зз) и хотите выяснить, все ли было сделано правильно. Но после ввода команды uulog может оказаться, что файл регистрации UUCP имеет размер 30 или 40 Кб, а вам бы не хотелось перелистывать страницы, с тем чтобы добраться до конца. Как быть? Команда tail — как раз то, что вам нужно в подобной ситуации. Она читает свой ввод и игнорирует все, кроме последних десяти строк (по умолчанию). Следовательно, если вы полностью уверены, что интересующая вас информация находится в конце файла, можете с помощью команды tail избавиться от ненужного "мусора". Вот как ее можно использовать совместно с командой uulog: % uulog | tail Вы получите последние десять строк файла регистрации утилиты UUCP. Как увидеть более десяти строк, вы узнаете из параграфа 25.15. 386 Часть четвертая. Содержимое файлов
zs:i6 Команде tail можно предоставить только одно имя файла: % tail файл Есть много других ситуаций, в которых может пригодиться команда tail. Я применял ее, желая убедиться, что задание, создающее большой выходной файл, завершилось корректно, чтобы вспомнить, о чем шла речь в последних почтовых сообщениях, находящихся в моем почтовом ящике, и т.д. Вы оцените важность команды tail, когда вас будут интересовать последние строки какого-нибудь файла. - ML 25.15 Управление командой tail А что если нужно просмотреть последние 11 строк файла? Команда tall -л показывает последние л строк. Команда tail +л игнорирует первые л-1 строк, выводя строку с номером л и все, что находится ниже. Можно также попросить команду tail подсчитать количество символов или блоков, содержащих по 512 байтов. Чтобы это сделать, используйте опцию -с (подсчет символов) или -Ь (подсчет блоков). При необходимости явно указать определенные строки можно воспользоваться опцией -/. Примечание: Команда /«//является одной из тех UNIX-программ, которые "любят", чтобы их аргументы были объединены. Иначе говоря, при необходимости просмотреть последние три блока файла введите команду tail -ЗЬ. Если вы зададите команду tail -3 -b, то команда tail решит, что вы хотите просмотреть файл -Ь. Это же касается опции -/ речь о которой пойдет в параграфе 25.16. Например, команда tail -4b mail.txt выведет на экран последние 2048 байтов (4 блока по 512 байтов) моего файла mail.txt. Многие версии команды tail имеют также опцию -г, которая показывает содержимое файла в обратном порядке, начиная с последней строки (см. также параграф 25.19). Некоторые версии UNIX ограничивают количество строк, которые может отображать команда tail, особенно при использовании опции -г. - ML 25.16 Как проследить за появлением новых строк в файле Одна из самых полезных особенностей команды tail — возможность отображать строки, добавляемые в какой-либо файл. Помню, однажды я отлаживал программу с именем totroff, которая преобразовывала руководство из простого текстового формата в формат troff. Программа работала довольно медленно, а мне хотелось посмотреть результат, не дожидаясь ее завершения. У меня не было желания водить через каждые 20 секунд команду more (25.Ю), с тем чтобы проверить, обработана ли отлаживаемая часть файла. (Утилита more завершается, когда вы выходите за пределы файла, поэтому она не годится для просмотра той его части, которая еще не записана.) Команда, tail -/ решает эту проблему: & 12.01 % totroff < file.txt > file.ms £ [1] 12345 % tail -f file.ms .LP Команда tail выводит строки по мере увеличения файла |CTRL-c| Просмотр содержимого файла 13* 387
25.17 Команда tail -/ может также использоваться для наблюдения за увеличением любого системного файла регистрации (/usr/adm/messages, файлы регистрации sendmail, файлы новостей и т.п.) Что в таком случае происходит? Команда tail -/при вызове читает файл и выводит последние 10 строк (или иное их количество) на экран. Однако в отличие от большинства других приложений команда tail после этого не завершается. Наоборот, она входит в бесконечный цикл: "засыпает" на секунду, затем "просыпается" и проверяет, не увеличился ли файл, после чего "засыпает" опять и т.д. Поскольку этот цикл бесконечен, то, ознакомившись с интересующими вас данными, а также после закрытия файла, за которым ведется наблюдение, необходимо нажать [CTRL-c] (или аналог этой комбинации в вашей системе (зью)). Команда tail не имеет возможности узнать о том, что файл больше не увеличивается. Команда tail игнорирует опцию -/ если она читает данные из канала. Так, команда totrqff < file.txt | tail -/работать не будет. - ML 25.17 Замена для команды tail Некоторые версии UNIX System V не поддерживают команду tail ps.uy. Приведем определения функции и псевдонима этой команды, названных ptail и являющихся ее аналогами: ptail() { sed -е :а -е '$q;N;11,$D;ba' $1; } alias ptail sed -e :a -e \'•$q;N;11,$D;ba'\' jj-jjjj При этом выводятся последние десять строк файла. (Эту функцию и псевдоним написал Грег Уббен.) - JP И ch tnit 25.18 Наблюдение за увеличением объема нескольких файлов Рассмотрим еще один полезный инструмент. Предположим, вам нужно следить за объемом нескольких файлов одновременно. Администраторам, например, может понадобиться наблюдать за файлами регистрации, такими как /usr/adm/messages и /usr/adm/lpd-errs, файлом ошибок UUCP и некоторыми другими. И Когда нужно следить сразу за несколькими административными файлами регистрации, очень кстати оказывается программа xtall. Однако она полезна не только для администраторов. Эта программа похожа на команду tail -/, но отслеживает несколько файлов сразу. xt3il Предположим, нам нужно произвести обработку многих файлов с помощью нескольких команд grep с сохранением результатов в различных файлах: % grep Berkeley ch?? > Berkeley.grep £ % grep BSD ch?? > BSD.grep £ % grep "System V" ch?? > SystemV.grep £ % grep SysV ch?? > SysV.grep £ % xtall Berkeley.grep BSD.grep SystemV.grep SysV.grep Новый текст, появившийся в файлах, которые указаны в аргументах команды xtail, также отображается на экране: *** SysV.grep *** chOl:using a SysV-based UNIX system, you must *** Berkeley.grep *** ch01:at the University of California at Berkeley, where *** BSD.grep *** ch03:prefer BSD UNIX systems because they are less likely to 388 Часть четвертая. Содержимое файлов
25.20 ch04:who use a BSD-based UNIX systems must run the *** SysV.grep *** ch04:is a SysV derivative sold by Acme Products Inc. (Имя нового файла, в который записывается текст, выводится окруженным звездочками.) Если вы нажмете клавишу прерывания (S.ov) (обычно это [CTRL-c] или [DEL]), программа xtail сообщит, какие файлы были модифицированы последними: |CTRL-"c| *** recently changed files *** 1 4-NOV-92 18:21:12 BSD.grep 2 4-NOV-92 18:19:52 Berkeley.grep 3 4-NOV-92 17:42:45 SysV.grep Чтобы выйти из программы xtail, необходимо послать сигнал QUIT (обычно [CTRL-\]). Если аргумент, переданный программе xtail, является именем каталога, а не файла, то программа будет следить за всеми файлами данного каталога. Эта ее особенность очень ценна для администраторов, так как позволяет, в частности, следить за файлами регистрации утилиты UUCP (из) во всех подкаталогах каталога /usr/spool/uucp/.Log: % xtail /usr/spool/uucp/.Log/* - LM 25.19 Обратный порядок следования строк: программа flip Если вы просматриваете длинный файл регистрации и хотите видеть первыми самые последние его строки, рекомендуем воспользоваться командой tail -r ps.is). Она показывает строки файла в обратном порядке, с последней строкой на первом месте. Однако эта программа откажется работать, если файл будет слишком большим. Вы спросите, что значит "слишком большой"? Ответ зависит от вашей версии команды tail. Чтобы это выяснить, попробуйте поработать с очень длинным текстовым файлом и посмотрите, как далеко "зайдет" команда tail -r: % tail -r /usr/dict/words > /trap/words.tailr % Is -1 /trap/words.tailr /usr/dict/words -rw-r—r— 1 jerry 32768 Dec 5 09:49 /tmp/words.tailr -rw-r—r— 1 root 206672 Feb 8 1990 /usr/dict/words Команда tail -г в нашей системе завершилась после вывода 32768 символов. Программа же flip теоретически не имеет предела, но ей необходим достаточно большой объем системной памяти для хранения вашего файла (или стандартного ввода). Вот эта программа, любезно предоставленная Томом Кристиансеном (Tom Christiansen): #! /usr/local/bin/perl print reverse <> - JP 2520 Вывод начала файла Многие версии BSD UNIX содержат полезную программу head, которая выводит первые п (по умолчанию 10) строк файла. Пользователи System V или те, кто не имеет программы head, могут эмулировать ее поведение с помощью редактора sed. Проще всего использовать команду q редактора sed (34.21): % sed 10q файл Если хотите развлечься, попробуйте применить сценарий, чтобы полностью эмулировать поведение BSD-команды head, в том числе воспользоваться опцией для подсчета количества выводимых строк и вывода разделяющей строки — в случае, если в одной командной строке задано несколько имен файлов. Просмотр содержимого файла 389
25.21 Этот сценарий имеется на компакт-диске. Большая его часть очевидна, а один интересный фрагмент приведен ниже. Это команда sed, которая выводит разделитель, когда отображается следующий файл: sed " li\\ ==> $1 <== ${show}q" $1 Команда li редактора sed вставляет разделитель перед первой строкой. Команда q завершает работу редактора sed после вывода заданного количества строк (по умолчанию 10), указанного в переменной интерпретатора shell (6.os) $show. Интерпретатор shell заменяет переменную $1 именем читаемого файла. Двойные кавычки вокруг команд редактора sed позволяют интерпретатору shell конструировать команды по ходу действия, перед запуском редактора sed. - JP, TOR 25.21 Нумерация строк Иногда бывает необходимым распечатать файл с пронумерованными строками — например, если вы хотите привести сценарий или программу в документации и при обсуждении ссылаться на определенные строки. Это одна из наиболее удобных возможностей, предоставляемых командой cat с опцией -л. Команда cat -и предваряет появление каждой строки несколькими начальными пробелами, номером строки и символом табуляции. Причем количество начальных пробелов зависит от текущего значения номера строки. Номера строк выводятся в колонке шириной в 6 символов и выравниваются по правому краю колонки. Максимальное значение номера строки является 6-разрядным числом. Я говорю об этом столь подробно на тот случай, если вы захотите подобрать количество начальных пробелов с помощью команды, подобной cut (js.N). Если ваша версия команды cat не поддерживает опцию -и, попробуйте задействовать программу нумерации строк л/. Команда nl -ba работает подобно cat -п. Сама по себе эта команда нумерует только строки с текстом. Подобного результата можно достичь и с помощью команды pr -t -п. (Опция -и предотвращает вставку командой рг заголовка и нижнего колонтитула (43.оп), которые обычно используются для разбивки ее вывода на страницы.) И раз уж мы предоставляем вам выбор, то вот еще четыре варианта : -) от Грега Уббена: grep -n \А awk '{print NR,$0}' sed = I sed 'N;s/\n/ /• ex - ■+%#\|qI - JP, TOR 390 Часть четвертая. Содержимое файлов
26 Регулярные выражения (шаблоны) 26.01 Это выражение Пытаясь объяснить своей маленькой дочери значение какого-то идиоматического выражения типа "любопытной Варваре на базаре нос оторвали", я вынужден был рассказывать ей, что это — выражение, которое не следует понимать буквально. Как следствие этого, она начала говорить: "Это просто такое выражение", комментируя собственные замечания, особенно когда не совсем понимала смысла сказанного. Выражение (даже в компьютерной терминологии) не должно интерпретироваться буквально. Это нечто, требующее вычисления. Многие UNIX-программы используют специальный синтаксис регулярных выражений для описания того, что можно себе представить как шаблоны поиска с метасимволами. Регулярные выражения описывают шаблоны, или цепочки символов, не обязательно буквально задавая эти символы. Иногда такой механизм называют сравнением с шаблоном. В данной главе мы немного отойдем от стиля "советов и трюков" и представим в параграфе 26.04 расширенное руководство по регулярным выражениям. Объясняется это тем, что понимание регулярных выражений чрезвычайно важно для выполнения предлагаемых советов и трюков. Мы решили поговорить об этом детально. Параграф-руководство сопровождается несколькими советами (параграфы 26.05 и 26.07), а также описанием нескольких инструментальных средств, которые помогают проверить, чему соответствуют различные выражения (парафаф 26.06). Здесь есть также краткий справочник (параграф 26.10) для тех, кто хочет просто что-то освежить в памяти. Перечислим главы, в которых представлены советы, приемы и описания инструментальных средств, предполагающие понимание синтаксиса регулярных выражений: • глава 27 "Поиск в файлах"; • глава 30 "Особенности редактора vi"; • глава 33 "Пакетное редактирование"; • глава 34 "Потоковый редактор sed"; • глава 37 "Язык Perl". Книга Джеффри Фридля (Jeffrey Friedl) Mastering Regular Expression издательства O'Reilly & Associates является буквально золотой россыпью примеров и находок. — DD, TOR (Дейл написал большую часть книги sed & awk издательства O'Reilly & Associates) 26.02 Не путайте регулярные выражения с метасимволами Прежде чем начать разговор о регулярных выражениях, сделаем предупреждение для начинающих: регулярные выражения могут сбить с толку, поскольку они напоминают шаблоны имен файлов, используемые интерпретатором shell. И в интерпретаторах shell, и в профаммах, применяющих регулярные выражения, такие символы, как звездочка (*), вопросительный знак (?), круглые скобки (О), квадратные скобки ([]), вертикальная черта Регулярные выражения (шаблоны) 391
Z6.U3 (I, "канал"), имеют специальное назначение. Некоторые из этих символов даже выполняют похожие действия. Вспомните, например, что команды интерпретатора shell find и cpio для сравнения с именами файлов используют шаблоны, которые не являются регулярными выражениями. Вы, наверное, не забыли и о том, что перед передачей аргументов программе интерпретатор shell выполняет подстановку метасимволов. Для предотвращения такой подстановки специальные символы в регулярных выражениях должны быть взяты в кавычки (s.u), если они передаются в качестве аргументов. В частности, команда $ grep [A-Z]*.c chap[12] может быть интерпретирована как grep Array.с Bug.с Сотр.с chapl chap2 и тогда команда grep вынуждена будет искать строку Array.с в файлах Bug.c, Comp.c, chap] и chap2. Самым простым решением в большинстве случаев является взятие регулярного выражения в одинарные кавычки ('). - ВВ, DG, TOR 26.03 Как понимать выражения Вы, наверное, знакомы с выражениями, которые вычисляются с помощью калькулятора. Приведем пример простого арифметического выражения: 2 + 4 Выражение "два плюс четыре" состоит из двух констант, или литералов, и оператора. Программа калькулятора должна распознать, например, что 2 является числовой константой, а знак "плюс" представляет оператор, который не нужно интерпретировать как символ +. Выражение объясняет компьютеру, как получить результат. Хотя это всего лишь сумма "два плюс четыре", которую мы хотим вычислить, мы не просим компьютер просто вернуть число 6. Мы заставляем его вычислить выражение и вернуть его значение. Выражение может быть и более сложным, скажем, состоять из нескольких простых выражений: 2 + 3*4 Калькулятор обычно производит вычисление выражения в направлении слева направо. Однако некоторые операторы имеют приоритет перед другими, т.е. выполняются первыми. Так, приведенное выше выражение имеет значение 14, а не 204 поскольку операция умножения должна предшествовать операции сложения. Приоритеты могут быть изменены путем помещения простого выражения в скобки. Так, значение выражения (2+3)*4, или "сложить 2 и 3 и умножить на 4", равно 20. Скобки сообщают калькулятору, что нужно изменить порядок вычисления выражения. Регулярное выражение — это, можно сказать, описание шаблона или цепочки символов. Конкатенация является основной операцией, используемой в регулярном выражении: АВЕ Каждый символ в данном случае является регулярным выражением, которое соответствует только этому одному символу. Приведенное выше выражение описывает "А, затем — В, затем — Е", или просто строку АВЕ. Понятие строка подразумевает группу сцепленных друг с другом символов. Наше регулярное выражение описывает цепочку символов и только. (Начинающие пользователи склонны применять термины более высокого порядка, такие как слова, а не отдельные символы.) В регулярных выражениях всегда имеет значение регистр: символ А не соответствует символу а. 392 Часть четвертая. Содержимое файлов
26.03 Программы, принимающие в качестве аргументов регулярные выражения, и в частности утилита grep (27.02), прежде чем создать шаблон, должны сначала оценить синтаксис выражения. После этого они построчно читают ввод и пытаются найти соответствие шаблону. Входные данные являются строками, поэтому, чтобы проверить, соответствует ли данная строка шаблону, программа сравнивает первый символ строки с первым символом шаблона. Если совпадение обнаруживается, то сравниваются вторые символы строки и шаблона. В случае отсутствия совпадения второй символ строки сравнивается с первым символом шаблона и т.д. На рис. 26.1 проиллюстрирован процесс, в котором шаблон abe сравнивается с входной строкой. Строка символов (входная строка) The canister must be labeled. Шаблон посимвольно сравнивается с входной строкой Tlhe abe Совпадения первых символов входной строки и шаблона не отмечено, поэтому следующий символ входной строки сравнивается с первым символом шаблона. llabeled alnister abe Первый раз символ строки совпадает с первым символом шаблона в слове "canister". Поскопьку совпадение имеется, то далее сравнивается второй символ шаблона со следующим символом входной строки. abe laBeled аЫе Следующее совпадение первого символа шаблона отмечается в слове "labeled". Поскольку совпадение имеется, то далее сравниваются второй символ шаблона и следующий символ входной строки. И на этот раз совпадение есть. Строка abe (шаблон) ГаБё! са п аЫ ister е Второй символ шаблона не совпадает со следующим символом входной строки. Поэтому возвращаемся к первому символу шаблона и сравниваем его со следующим символом входной строки. 1 led Теперь третий символ шаблона сравнивается со следующим символом входной строки. Они также совпадают. Итак, входная строка соответствует шаблону. Рис. 26.1. Интерпретация регулярного выражения Регулярные выражения могут содержать не только буквы. В частности, в них может присутствовать такой символ, как точка (.), которую можно использовать как метасимвол, соответствующий любому отдельному символу. Использование этого метасимвола аналогично применению фишки со звездочкой в игре "Эрудит", которая способна заменить любую букву. Так, регулярное выражение А.Е является эквивалентом выражений АСЕ, ABE или, скажем, ALE — точка соответствует любому символу после А. Метасимвол * (звездочка) используется для обозначения соответствия любому количеству (в том числе 0) экземпляров предыдущего регулярного выражения, которое обычно задается в виде одного символа. Звездочка вам, должно быть, знакома и как метасимвол интерпретатора shell, где она также может обозначать "любое количество символов". Но это значение сильно отличается от такового в регулярных выражениях. Здесь метасимвол * сам по себе не задает никакого шаблона; он лишь модифицирует то, что идет перед ним. Регулярное выражение . * соответствует любому количеству символов, а выражение А.*Е — любой строке, которая задается выражением А.Е, а также любому количеству символов между А и Е, например AIR-PLANE, A FINE, AE, A 32-cent S.A.S.E или A LONG WAY HOME. Регулярные выражения (шаблоны) 393
26.04 Если вы понимаете разницу между . и * в регулярных выражениях, то вы уже знаете два основных типа метасимволов: те, которые соответствуют одному символу, и те, которые изменяют способ интерпретации предыдущих символов. Вам, наверное, ясно, что путем использования метасимволов можно как расширить, так и сузить диапазон возможных совпадений. Причем этим процессом можно управлять. В параграфе 26.04 Брюс Барнетт подробно объясняет, как метасимволы используются в регулярных выражениях. — DD, из книги sed & awk издательства O'Reilly & Associates 26.04 Использование метасимволов в регулярных выражениях Регулярное выражение состоит из трех основных частей: 1) якоря, который используется для задания позиции шаблона по отношению к строке текста; 2) наборов символов, соответствующих одному или нескольким символам в определенной позиции; 3) модификатора, задающего количество повторов предыдущего набора символов. Следующий простой пример демонстрирует все три части регулярного выражения: "#* Символ А является якорем, указывающим на начало строки, символ # — простым набором символов, который в данном случае соответствует только одному символу #, а звездочка (*) — модификатором. В регулярном выражении она означает, что предыдущий символ может встречаться сколько угодно раз, в том числе и ни разу. Как вы вскоре узнаете, это регулярное выражение бесполезно (разве что на его примере можно проиллюстрировать синтаксис). Существует два основных типа регулярных выражений: простые и расширенные. (Как мы сможем убедиться ниже, граница между ними по мере усложнения регулярных выражений становится все менее четкой.) Регулярные выражения применяются рядом утилит, в том числе и такими, как awk и egrep. Большинство из них используют простые регулярные выражения. Далее, когда речь будет идти о регулярном выражении (без уточнения, простое оно или расширенное), то будут описываться общие возможности обоих типов. Сейчас же следует отметить, что команды vi, sed, grep, csplit, dbx, more, ed, expr, lex и pg понимают только простые регулярные выражения, а утилиты awk, nawk и egrep — расширенные регулярные выражения. [Ситуация усложняется тем, что простые регулярные выражения с течением времени усложнились, в результате чего появились версии "простых регулярных выражений", которые поддерживают расширения, отсутствующие в расширенных регулярных выражениях! Брюс объясняет это несоответствие в конце своего параграфа. — TOR] Символы обозначения позиции Большинство средств UNIX при обработке текстов оперирует символами, находящимися в пределах одной строки. Обнаружить совпадение с шаблоном, простирающимся на несколько строк, непросто. Дело в том, что символ конца строки не включается в блок текста, в котором ведется поиск. Он служит разделителем. Регулярные выражения исследуют текст между разделителями. Для поиска символов, соответствующих шаблону, в начале или в конце строки используются якоря. Символ А является начальным якорем, а символ $ — конечным. Регулярное выражение ЛА будет соответствовать всем строкам, начинающимся с А, а регулярное выражение А$ — всем строкам, заканчивающимся на А. Если символ якоря не стоит в начале или конце шаблона, то он не служит якорем. Это значит, что символ А считается якорем только тогда, когда он есть первый символ регулярного выражения. Символ $ можно считать якорем только в том случае, если он является последним символом. Выражение $1 не содержит якоря, равно как и выражение 1Л. Если нужно сравнивать символ Л в начале строки или символ $ в конце, необходимо изменить назначение специального символа, набрав перед ним обратную косую черту (\). Примеры, иллюстрирующие сказанное, приведены в табл. 26.1. 394 Часть четвертая. Содержимое файлов
26.04 Таблица 26.1. Варианты обозначения позиции в регулярном выражении Шаблон ЛА А$ Ал $А л\л Л Л \$$ $$ Позиция в строке А в начале строки А в конце строки АЛ в любом месте строки $А в любом месте строки Л в начале строки То же, что Л\Л $ в конце строки То же, что \$$ Соглашение по использованию символов А и $ в качестве указателей начала и конца строки соблюдается и в ряде утилит. В редакторе w оба символа применяются как команды перехода в начало и в конец строки. В интерпретаторе С shell выражение !" предназначено для обозначения первого аргумента предыдущей командной строки, а выражение ! $ соответствует последнему аргументу предыдущей строки (см. параграф 11.07). Указанное соглашение соблюдается во многих утилитах с целью поддержания согласованности. Например, в редакторах ed и sed символ $ может обозначать последнюю строку файла. Команда cat -v -e (2S.o6,2s.<i7) символом $ помечает конец строк. Указанный символ встречается и в других программах. Сравнение символов с помощью наборв символов Простейшим набором символов является один символ. Регулярное выражение the содержит три набора символов: t, h и е. Оно будет соответствовать любой строке, содержащей the, в том числе строке word other. Чтобы предотвратить это, поместите в начале и в конце шаблона символы пробела (обозначим его как D): DtheD. Вы можете скомбинировать строку и якорь. Шаблон AFrom:D будет соответствовать, в частности, тем строкам почтового сообщения р.зз), в которых указан отправитель. Используйте этот шаблон с командой grep для вывода всех адресов, содержащихся в почтовом ящике поступлений: $USER 6.03 % grep '"From: ' /usr/spool/mail/$USER Некоторые символы в регулярных выражениях имеют специальное назначение. Если нужно найти сами эти символы, измените их назначение с помощью обратной косой черты (\). Совпадение с любым символом Точка (.) является одним из метасимволов. Она может соответствовать любому символу, кроме символа конца строки. Шаблон Л. $ будет соответствовать строке, содержащей любой один символ. Задание диапазона символов Если нужно обеспечить совпадение с определенными символами, используются квадратные скобки ([]), позволяющие определить, какие именно символы нужно искать. Шаблон Л [0123456789]$ соответствует любой строке текста, содержащей ровно одну цифру. Этот шаблон может быть короче. Для определения диапазона символов можно поставить между двумя символами дефис: Л [ 0-9 ] $. Можно также сочетать конкретные символы с диапазонами символов. Шаблон [A-Za-zO-9] соответствует одному символу, который является буквой, цифрой или знаком подчеркивания. Наборы символов можно комбинировать, помещая их рядом. Допустим, нужно найти слово по следующим признакам: • начинается с т; • является первым словом в строке; • вторая буква — строчная; Регулярные выражения (шаблоны) 395
2SM- • состоит из трех букв, после которых идет пробел (□); • третья буква — строчная гласная. В этом случае регулярное выражение может быть таким: "T[a-z] [aeiou]D. [Уточним некоторые моменты. Диапазон символов содержит непрерывный ряд всех символов, лежащих в его границах. Порядок их следования определяется таблицей ASCII-кодов (51.оз). Например, [z-a] не является диапазоном символов, поскольку крайние символы указаны в обратном порядке. Диапазон [A-z] соответствует не только прописным и строчным буквам, но также шести символам, которые попадают между прописными и строчными буквами в таблице ASCII-кодов: [, \, ], Л, _ и \ — JP\ Исключение из набора символов При необходимости задать поиск всех символов, кроме указанных, последние нужно поместить в квадратные скобки с символом Л сразу после левой скобки. Чтобы найти все символы, кроме гласных строчных букв, следует воспользоваться шаблоном [Aaeiou]. По аналогии с символами обозначения позиции правая квадратная скобка (]) и дефис (-) не имеют специального назначения, если они идут сразу после левой квадратной скобки ([). Рассмотрим примеры, представленные в табл. 26.2. Таблица 26.2. Наборы символов регулярного выражения Регулярное выражение [0-9] [Л0-9] [-0-9] [0-9-] [Л-0-9] []0-9] [0-9]] [O-99-z] П0-9-] Чему соответствует Любой цифре Любому символу, кроме цифры Любой цифре или символу - Любой цифре или символу - Любому символу, кроме - и цифры Любой цифре или символу ] Любой цифре, после которой следует ] Любой цифре и любой букве, находящейся в диапазоне между 9 И Z (51.03) Любой цифре, символу - или ] Повторение символов Третьей частью регулярного выражения является модификатор. Он предназначен для задания количества повторов предыдущего набора символов. Специальный символ * (звездочка) соответствует нескольким или ни одной копии. Это значит, что регулярное выражение 0* соответствует либо ни одному, либо нескольким нулям, а выражение [0-9]* — либо ни одной, либо нескольким цифрам. Из этого понятно, почему шаблон л#* считается бесполезным: он подразумевает наличие любого количества символов # в начале строки, в том числе нулевого. Следовательно, этот шаблон совпадет с любой строкой, потому что в начале каждой строки есть либо нуль символов #, либо больше. Может показаться, что начинать счет символов с нуля глупо. Но это совсем не так. Поиск заранее не известного количества символов имеет определенный смысл. Предположим, во-первых, что вы хотите найти цифру в начале строки, а во-вторых, что перед цифрой могут быть, а могут и не быть пробелы. В данном случае есть смысл использовать шаблон ЛП*, который соответствует любому количеству (в том числе 0) пробелов в начале строки. Если нужно найти не менее одного символа, повторите набор символов. Это значит, что выражение [0-9] * соответствует нулевому и большему количеству цифр, а выражение [0-9] [0-9] * — одной или нескольким цифрам. 396 Часть четвертая. Содержимое файлов
26.04 Соответствие определенному количеству наборов символов С помощью модификатора * невозможно задать максимальное количество наборов символов. Однако некоторые программы (2б.пя) распознают специальный шаблон, который можно применять для задания минимального и максимального количества повторений набора символов. Это делается путем помещения соответствующих двух чисел между символами \{ и \}. Дабы убедить вас в том, что сочетание символов \ { не является ошибкой, приведем пример. Регулярное выражение [a-z]\{4,8\} соответствует четырем, пяти, шести, семи или восьми строчным буквам. Можно использовать любое число из диапазона от 0 до 255. Второе число можно опустить — в таком случае верхний предел не будет задан. Если будет опущена запятая и второе число, то регулярное выражение задаст точное количество повторов шаблона. Символ обратной косой черты заслуживает отдельного обсуждения. Обычно косая черта отменяет специальное назначение следующего за ней символа. Например, точке как символу соответствует \., а звездочке — \*. Однако обратная косая черта, стоящая перед символами <, >, {, }, (, ) или цифрой, разрешает их специальную интерпретацию. Объясняется это тем, что перечисленные специальные символы были введены намного позже, чем появились регулярные выражения. Изменение назначения символов {, }, (, ), < и > могло бы исказить старые выражения. (Подобное тяжкое преступление должно караться годом каторжных работ, заключающихся в написании программ на COBOL. :-) ) Вместе с тем добавление обратной косой черты расширяет функциональные возможности программного кода, не разрушая старые программы. И я советую вам не ворчать по поводу этого изменения, а считать его издержкой эволюции. Следует помнить, что модификаторы * и \{1,5\} являются таковыми лишь при условии, что они следуют за набором символов. Если бы они находились в начале строки, то модификаторами не были бы. Примеры использования модификаторов приведены в табл. 26.3. Таблица 26.3. Примеры повторения шаблонов в регулярных выражениях Регулярное выражение * \* \\ л*. ЛА* ЛА\* ЛАА* ААА*В ЛА\{4,8\}В ЛА\{4,\}В ЛА\{4\}В \{4,8\} А{4,8) Чему соответствует Любой строке, содержащей * Любой строке, содержащей * Любой строке, содержащей \ Любой строке, начинающейся с * Любой строке Любой строке, начинающейся с А* Любой строке, начинающейся с А Любой строке, начинающейся с одного или нескольких А с последующим В Любой строке, начинающейся с четырех, пяти, шести, семи или восьми А с последующим в Любой строке, начинающейся с четырех и более А с последующим в Любой строке, начинающейся с ААААВ Любой строке, содержащей {4,8} Любой строке, содержащей А{4,8} Регулярные выражения (шаблоны) 397
26.04 Сравнение слов Поиск слов не является таким простым делом, как это может показаться на первый взгляд. Например, шаблон the соответствует слову other. Можно поместить пробел до и после букв и использовать регулярное выражение DtheD. Однако это выражение не соответствует словам в начале и в конце строки. Кроме того, оно не будет соответствовать слову со знаком препинания после него. Существует простой выход из создавшегося положения, по крайней мере во многих версиях редакторов ed, ex и v/. Символы \< и \> подобны указателям позиции Л и $. Эти символы позиционируют находящееся между ними выражение таким образом, чтобы оно давало совпадение только тогда, когда подходящие символы находятся на границе слова. Так, шаблон \<[tT]he\> позволяет искать слова the и The. Определим понятие границы слова. Перед символом t или Т должен стоять либо символ новой строки, либо любой другой символ, кроме буквы, цифры или знака подчеркивания (_). После символа е должен идти символ конца строки или любой другой символ, кроме буквы, цифры или знака подчеркивания. Запоминание шаблонов Еще одной задачей, требующей наличия специального механизма, является поиск повторяющихся слов. Выражение [a-z] [a-z] соответствует двум прописным буквам. Если нужно найти строки, содержащие сдвоенные буквы, то такой шаблон не поможет. Здесь нужно как-то запомнить найденное и проверить, не попадется ли тот же самый шаблон вновь. В некоторых программах можно запомнить часть шаблона, пометив ее операторами \ ( и \). Тогда можно повторно вызвать запомненный шаблон с помощью оператора \ и последующей одной цифры. Следовательно, для поиска двух одинаковых букв подойдет шаблон \ ([a-z] \) \1. Всего можно иметь девять запомненных шаблонов. Каждый экземпляр символов \ ( начинает новый шаблон. Регулярное выражение для поиска палиндромов из пяти букв (например, "радар") выглядит так: \ ([a-z] \) \ ([a-z] \) [a-z] \2\1. [Отдельные версии некоторых программ не могут обрабатывать операторы \ ( и \) в том регулярном выражении, в котором содержится оператор \1. Во всех версиях редактора sed операторы \ ( и \) без проблем можно использовать в шаблонной части команды s, а оператор Ми некоторые другие — в заменяющей ее части (З4.щ. — JP\ Возможные проблемы На этом мы завершаем обсуждение простых регулярных выражений. Прежде чем перейти к знакомству с дополнительными возможностями, предоставляемыми расширенными регулярными выражениями, я хотел бы упомянуть о двух возможных проблемах. Операторы \( и \) впервые появились в редакторе W. Другие программы на то время не обладали такой возможностью. Новыми являются также модификаторы \{min,max\}, которых не было в прежних утилитах. Это создает трудности для начинающих пользователей: у них создается впечатление, что каждая утилита отвечает различным соглашениям. Компания Sun пересмотрела библиотеку новейших регулярных выражений для своих программ, и теперь они все имеют одинаковые возможности. Попытавшись использовать эти новые возможности на машинах других поставщиков, вы обнаружите, что они работают по-иному. Еще одним источником недоразумений может стать диапазон совпадения шаблона (хм). Регулярные выражения обеспечивают совпадение с цепочкой символов максимально возможной длины. Это значит, что регулярное выражение А.*В соответствует ДАВ, равно как и последовательности символов ааааввввавсссвввааав. Это не вызывает особых проблем в случае применения команды grep, поскольку более широкий диапазон совпадений регулярного выражения просто приведет к совпадению большего количества строк, чем требовалось. Если вы используете редактор sed и ваши шаблоны дадут большое число совпадений, то дело закончится удааением или изменением большего количества строк, чем предполагалось. Расширенные регулярные выражения Расширенные регулярные выражения используются в двух программах: egrep и awk. [В языке Perl можно применять еще более расширенные выражения. — JP\ При работе с этими 398 Часть четвертая. Содержимое файлов
26.05 расширениями упомянутые выше специальные символы с предшествующей обратной косой чертой не имеют специального назначения. Речь идет о символах \ {, \}, \<, \>, \ (,'\), а также о выражении \цифра. Для этого есть серьезные основания, о которых мы поговорим позже. Вопросительный знак соответствует нулю или одному экземпляру набора символов, стоящих перед ним, а знак плюс — одному и более экземплярам набора символов. В расширенных регулярных выражениях нельзя использовать операторы \ { и \}, но можно применять оператор ? в качестве эквивалента выражения \(0,1\}, а оператор + — как эквивалент выражения \{1,\}. Возможно, вы удивлены тем, что расширенные регулярные выражения столь бесполезны. Их применение не дает никаких преимуществ, если не считать случаи использования символов ? и +, а недостатков они имеют предостаточно. Следовательно, пора приводить примеры. Самыми важными операторами в расширенных регулярных выражениях являются (, | и ). Скобки используются для группирования выражений, вертикальная черта действует как оператор ИЛИ. Вместе взятые они предоставляют возможность выбора шаблона. В качестве примера можете использовать программу egrep, предназначенную для вывода всех строк поступающей почты, которые содержат слово From: или Subject:: % egrep '"(From|Subject): ' /usr/spool/mail/$OSER Все строки, начинающиеся с From: или Subject:, будут выведены. Более простого способа выполнения этой задачи с помощью простых регулярных выражений не существует. Вы, конечно, можете попробовать воспользоваться выражением наподобие ~[FS] [ru] [ob] [mj ]e*c*t*: в надежде, что строки, начинающиеся со слова Sromeet:, вам не попадутся. Операторы \< и \> в расширенных регулярных выражениях не используются. Их отсутствие можно компенсировать применением альтернативного механизма. Сравнение слова the, находящегося в начале, середине или в конце предложения либо в конце строки, можно выполнить при помощи расширенного регулярного выражения (А| ) the( [Aa-z] | $). Перед этим словом могут находиться либо символ пробела, либо символ новой строки. После слова может идти символ конца строки или любой другой, кроме прописной буквы. Еще одним преимуществом при работе с расширенными регулярными выражениями следует считать возможность использования модификаторов *, + и ? после операторов группирования (...)• Вот как можно двумя способами сравнить фразы a simple problem, an easy problem и a problem (вторая команда более корректная): % egrep "a[n]? (simple|easy)? ?problem" data % egrep "a[n]? ((simple I easy) ) ?problem" data Я обещал объяснить, почему символ обратной косой черты не работает в расширенных регулярных выражениях. Возможно, операторы \ {... \} и \<... \> можно было бы добавить в расширенные регулярные выражения, но если при этом не будут добавлены и операторы \ (. . . \), то возникнет путаница. Кроме того, сделать это невозможно, не изменив существующих программ. Понимаете ли вы почему? Все очень просто. Если символ ( имеет специальное назначение, то \ ( должен быть обычным символом. В простых регулярных выражениях все наоборот: ( — это обычный символ, а \ ( — специальный. Следовательно, правила использования скобок стали бы несовместимыми, а любое их изменение могло бы привести к неработоспособности существующих программ. Если бы в расширенных выражениях символы (. . . | . . .) использовались как обычные, а символы \ (. . . \ | . . . \) применялись для задания альтернативного шаблона, то можно было бы создать набор полнофункциональных регулярных выражений. Между прочим, именно такой принцип реализован в редакторе GNU Emacs (З2.т), где для простых и расширенных регулярных выражений используется единый синтаксис. - ВВ 26.05 Как создать правильное регулярное выражение Для того чтобы написать правильное регулярное выражение, недостаточно знать теорию, ч Нужно научиться не только описывать шаблон, но и распознавать контекст, в котором он Регулярные выражения (шаблоны) 399
2&M сравнивается. Вам придется продумывать все детали регулярного выражения и строить его на основе контекста, в котором шаблон будет применяться. Разнообразие контекста, в котором может находиться шаблон, делает написание регулярного выражения задачей трудной, но в то же время и интересной. Это сложность того же порядка, что и сложность человеческого языка: часто бывает невозможно понять выражение (2e.oi), просто определив значение каждого слова с помощью словаря. Процесс написания регулярного выражения выполняется в три этапа. 1. Определение объекта поиска и преобразования. 2. Создание шаблона. 3. Проверка шаблона. Это практически та же последовательность, какой придерживаются программисты при разработке программ. Этап 1 можно рассматривать как разработку спецификации, которая бы отображала понимание проблемы и намечала пути ее решения. Этап 2 соответствует этапу кодирования, а этап 3 предполагает запуск программы и ее тестирование на соответствие спецификации. Этапы 2 и 3 образуют цикл, который повторяется до тех пор, пока программа не станет работать удовлетворительно. В процессе тестирования шаблона также необходимо убедиться, что регулярное выражение функционирует надлежащим образом. При этом изучение результатов тестирования, сравнение выходных данных с входными значительно улучшит ваше понимание регулярных выражений. Можно предложить следующий порядок рассмотрения результатов сравнения шаблона с текстом: Совпадения Строки, которые должны, совпадать Несовпадения Строки, которые не должны совпадать Пропущенные совпадения Строки, которые не совпали, но должны были совпасть Лишние совпадения Строки, которые совпали, но не должны были совпасть Попытки улучшить описание шаблона напоминают атаку с двух флангов: с одной стороны, путем сужения диапазона возможных совпадений мы пытаемся исключить лишние совпадения, а с другой, путем расширения упомянутого диапазона стараемся получить недостающие совпадения. Особенно часто трудности возникают при использовании для описания шаблонов фиксированных строк. Каждый удаляемый из шаблона символ увеличивает количество возможных совпадений- Например, при поиске строки what оказалось, что нужно также обеспечить совпадение со словом What. Единственной фиксированной строкой, которая совпадает и с what, и с what, является строка hat, самая длинная строка и общая для обоих случаев. Очевидно, что поиск с помощью шаблона hat приведет к нежелательным совпадениям. Добавление в шаблон, который содержит фиксированную строку, какого-либо символа уменьшает количество возможных совпадений. Строка them даст меньше совпадений, чем строка the. Использование в шаблонах метасимволов обеспечивает большую гибкость при сужении или расширении диапазона совпадений. Метасимволы, используемые в сочетании с литералами, могут применяться для расширения диапазона совпадения с одновременным исключением нежелательных совпадений. — DD, из книги sed & awk издательства O'Reilly & Associates 26.06 Чему соответствует регулярное выражение При изучении регулярных выражений сложнее всего определить, наборам каких символов они соответствуют. Проблема состоит в том, что регулярное выражение обеспечивает получение самой длинной из возможных совпадающих строк — она может быть значительно длиннее, чем вы предполагали. Приведем простой сценарий с именем showmatch, удобный для тестирования регулярных выражений, которые обычно создаются при написании jerf-сценариев Получив регулярное выражение и имя файла, он находит соответствующие строки — точно так же, как это делает ■—. : : Ч г—*К *—. ; ; 400 Часть четвертая, Содержимое файлов showmttcb
M06 команда grep. Правда, при этом для выделения части строки, которая совпадает на самом деле, используется строка символов ЛЛЛА. '%! /bin/tchsh # showmatch помечает строки, соответствующие шаблону pattern=$l; shift gawk { if (match($0,pattern)) { print patmatch = "" for (k = 1; к <= RLENGTH; k++) patmatch = patmatch "A" printf("%"RSTART -l"s" "%-s\n","",patmatch) } (' pattern='$pattern" $* Например: % showmatch 'CD-...' mbox and CD-ROM publishing. We have recognized that documentation will be shipped on CD-ROM; however, Сценарий xgrep, похожий на предыдущий, выводит только совпадающий текст. Это позволяет извлекать из файла данные, соответствующие шаблону. Можно, например, извлечь только числа из таблицы, содержащей текст и числа. Этот сценарий также прекрасно подходит для подсчета количества совпадений с шаблоном в тексте файла, как показано ниже. Убедитесь лишь, что ваш шаблон обеспечивает правильный поиск текста в файле. Если вы не уверены в корректности его работы, выполните команду wc и проанализируйте результат. Например, регулярное выражение [0-9] в таком числе, как 3.2, даст два совпадения: первый раз — с цифрой 3, второй — с цифрой 2! Здесь нужно было бы включить в шаблон точку (.) или запятую (,), что обычно зависит от способа записи чисел. Так, шаблон [0-9] [.0-9]* соответствует ведущей цифре, которая может сопровождаться точками и цифрами. Примечание: Не забывайте, что выражения типа [ 0-9] * соответствуют также нулевому количеству цифр (потому что * означает "от нуля и больше предыдущих символов"). Такое выражение может заставить сценарий xgrep работать очень долго! Поэтому лучше использовать следующее выражение, соответствующее одной млн нескольким цифрам: xgrep "[0-9] [0-9]*" файлы I wc -1 В сценарии xgrep запускаются команды редактора sed, приведенные ниже. Значением переменной $ге является регулярное выражение, полученное из командной строки, а значением переменной $х — символ [CTRL-b], используемый в качестве разделителя. Команды редактора sed пронумерованы (например, 5>) только для использования их в ссылках, в сценарий эти номера не входят: 1> \$x$re$x!d 2> s//$x&$x/g 3> s/[A$x]*$x// 4> з/$х[Л$х]*$х/\ /g 5> s/$x.*/7 Команда 1 удаляет все входные строки, не содержащие совпадений. В оставшихся строках (в которых совпадения имеются) команда 2 окружает совпадающий текст разделяющими
26.07 символами [CTRL-b]. Команда 3 удаляет все символы, включая первый разделитель, перед первым совпадением в строке. Если в строке есть больше одного совпадения, то команда 4 разбивает их на отдельные строки. Команда 5 удаляет из каждой выходной строки последний разделитель и текст после него. Сценарий xgrep написан Грегом Уббеном на основе сценария showmatch. - JP, DD, TOR 26.07 Ограничение размера совпадающей строки Регулярное выражение стремится обеспечить совпадения с как можно более длинной строкой, вследствие чего могут возникнуть неожиданные проблемы. Рассмотрим в качестве примера следующее регулярное выражение, соответствующее любому количеству символов, заключенных в кавычки: н +1» Предположим, у нас есть макрос программы troff, который содержит два аргумента, заключенные в кавычки: . Se "Appendix" "Full Program Listings" Чтобы обеспечить совпадение с первым аргументом, новичок мог бы описать шаблон с помощью такого регулярного выражения: \.Se ".*" Однако этот шаблон совпадет со всей строкой, поскольку вторая кавычка шаблона соответствует последней кавычке строки. Если известно количество аргументов, то можно задать регулярное выражение для каждого из них: \.Se ".*" ". * " Хотя это выражение будет работать нормально, в каждой строке может быть неодинаковое количество аргументов, а вам нужен только первый аргумент. Предлагаем другое регулярное выражение, соответствующее самой короткой из воаможных строк, расположенных между двумя кавычками: Ч[А|П*11 Оно соответствует "кавычке с любым последующим количеством символов, не являющихся кавычками, и с завершающей кавычкой". Использование так называемых исключаемых наборов символов, подобных приведенному выше, является одной из черт, отличающих профессионального пользователя регулярных выражений от начинающего. [В Perl 5 (37.05) добавлен новый, "не жадный" оператор регулярных выражений, способный обнаружить соответствие кратчайшей строке. — JP\ — DD, из книги sed & awk издательства O'Reilly & Associates 26.08 Примеры регулярных выражений Зная синтаксис регулярных выражений, можно найти соответствие чему угодно. Но иногда не хочется думать над тем, как достичь желаемого. Приведем несколько полезных регулярных выражений, которые могут соответствовать различным видам данных, встречающихся в среде UNIX. Некоторые из этих выражений способны работать в любой программе, использующей регулярные выражения, а некоторые — только в определенных программах, например в egrep. (Метасимволы, принимаемые всеми программами, перечислены в параграфе 26.09.) Символ D означает, что пробел является составной частью регулярного выражения. Заметим, что представленные регулярные выражения следует рассматривать только как примеры. Здесь не предполагается, что они будут соответствовать, скажем, каждому упоминанию названия города или штата в произвольном тексте. Однако четкое представление о том, что делает данное выражение и почему, позволит написать выражение, подходящее для вашего текста. 402 Часть четвертая. Содержимое файлов
26.09 Аббревиатура штата США (NM) D[A-z][A-z]D Город, штат (Portland, OR) A. *, D [A-z] [A-Z] Месяц, число, год (JAN 05, 1993) (January 5, 1993) [A-Z] [A-Za-z]\{2, 8\}D[0-9] \ fl,2\} ,D [0-9]\{4\ } Номер в системе социального страхования США (123-45-6789) [0-9]\{3\}-[0-9]\{2\}-[0-9]\{4\} Номер телефона в США (547-5800) [0-9]\{3\)-[0-9]\{4\] Форматированное денежное значение, в долларах ($1) ($ 1000000.00) \$П*[0-9] + (\. [0-9] [0-9])? Команды изменения шрифта в документе ttoff (\fR) (\f(CB) \\f [ (BIRP]C*[BW1* Запросы программы troff (.bp) A\. [a-z][a-z]D HTML-теги (<h2>) (<UL COMPACTS <[*>]*> Макросы программы troff (.Ah "Tips for" "ex&vi") A\.[A-Z12]. D".*" Пустые строки А$ Вся строка А . * $ Один или несколько пробелов □□* — DD, из книги sed & awk издательства O'Reilly & Associates 26.09 Метасимволы, действительные для различных UNIX-программ Некоторые метасимволы для регулярных выражений могут быть действительными в одних программах и недействительными в других. Метасимволы, используемые лишь в определенных программах, в табл. 26.4 помечены кружком (•). Краткое описание каждого символа можно найти в параграфе 26.10. [К сожалению, даже эта таблица не представляет полной картины. Например, компания Sun взяла некоторые расширения, первоначально разработанные для редакторов ed, ex и vi (в частности, модификаторы \< \> и \{мин,макс\ (), и добавила их в другие программы, использующие регулярные выражения. Поэтому не будьте особо доверчивыми, все проверяйте и не удивляйтесь, если какая-то возможность регулярного выражения не поддерживается отдельными программами. Кроме того, существует ряд программ, распознающих регулярные Регулярные выражения (шаблоны) 403
zs.m выражения, не указанные в таблице. К ним в первую очередь относятся такие программы, как perl, Emacs, more, dbx, expr, lex, pg, less.— ТОЩ Таблица 26.4. Метасимволы, действительные для различных программ Символ ed ex vi jed awk grep egrep Действие Соответствует любому символу Соответствует нулю или большему количеству предыдущих символов Соответствует началу строки Соответствует концу строки Изменяет назначение следующего символа Задает набор символов Запоминает шаблон для последующего применения Задает количество повторений символов Соответствует началу или концу слова Соответствует одному или нескольким предыдущим символам Соответствует ни одному или одному предыдущему символу Разделяет варианты для сравнения Группирует выражения $ \ Г ] \( \) \{ \) \< \> + ( ) Обратите внимание, что в редакторах ed, ex и sed необходима задавать как шаблон поиска (слева), так шаблон замены (справа). Метасимволы, приведенные в табл. 26.4, имеют указанное назначение только в шаблонах поиска. Редакторы ed, ex и sed поддерживают дополнительные метасимволы, которые действительны только в шаблонах замены (табл. 26.5). Таблица 26.5. Метасимволы, действительные в шаблонах замены Символ ex sed ed Действие \ \1 \L \Е \е Изменяет назначение следующего символа Повторно использует шаблон, запомненный с помощью оператора \ ( \) Повторно использует предыдущий шаблон поиска Повторно использует предыдущий шаблон замены Изменяет регистр символов на верхний Изменяет регистр символов на нижний Отменяет действие предыдущего оператора \и или \L Отменяет действие предыдущего оператора \и или \1 - DG 26.10 Краткий справочник по шаблонам В параграфе 26.04 представлены элементарные сведения о регулярных выражениях. Данный параграф предназначен для тех, кому время от времени необходимо просто освежить в памяти синтаксис регулярных выражений. Здесь также приведен ряд простых примеров. Символы, перечисленные в табл. 26.6, имеют специальное назначение только в шаблонах поиска. 404 Часть четвертая. Содержимое файлов
26. Ю Таблица 26.6. Специальные символы, используемые в шаблонах поиска Шаблон Предназначение Соответствует любому одиночному символу, кроме символа новой строки * Соответствует любому количеству (в том числе нулевому) повторов предшествующего символа. Предшествующий символ также может быть регулярным выражением. Например, поскольку . (точка) соответствует любому символу, то . * означает соответствие любому количеству любых символов А Соответствует последующему регулярному выражению в начале строки $ Соответствует предшествующему регулярному выражению в конце строки [ ] Соответствует любому из символов, указанных между скобками. Дефис (-) обозначает диапазон символов. Символ А, стоящий в начале списка символов в скобках, изменяет смысл на обратный: шаблон начинает соответствовать любому символу, не входящему в список. Дефис и правая квадратная скобка (]) являются членами списка, если они стоят в начале списка. Все другие метасимволы также рассматриваются в качестве членов списка Соответствует заданному количеству повторов предшествующего символа. Предшествующий символ тоже может быть регулярным выражением. Шаблон \ {п\} соответствует точно п символам; \ {л, \} — не менее чем л символам; \{л#,т\) — от л до л? символам Отменяет специальное назначение следующего символа Сохраняет в буфере строку, найденную с помощью шаблона, заключенного между \ ( и \). В одной строке можно применить до девяти буферов. Они могут быть повторно использованы в качестве шаблонов при помощи операторов \1 ... \9 Соответствует символам в начале (\<) или в. конце (\>) слова Соответствует одному или нескольким экземплярам предыдущего регулярного выражения ? Соответствует любому количеству (в том числе нулевому) экземпляров предыдущего регулярного выражения I Соответствует регулярному выражению, записанному слева или справа от этого символа ( ) Используется для сравнения с группой регулярных выражений, заключенных в скобки \{n,.m\} \ \( \) \< \> + Символы, приведенные в табл. 26.7, имеют специальное назначение только в шаблонах замены. Таблица 26.7. Специальные символы, используемые а шаблонах замены Шаблон Предназначение \ \л \и \и \1 \Ь Отменяет специальное назначение следующего символа Повторно использует л-й шаблон, запомненный с помощью оператора \ ( \); л является числом из диапазона от 1 до 9, где 1 соответствует самому первому шаблону Повторно использует шаблон поиска в качестве части шаблона замены Повторно использует предыдущий шаблон замены в текущем шаблоне замены Преобразует первый символ шаблона замены в символ верхнего регистра Преобразует символы шаблона замены в символы верхнего регистра Преобразует первый символ шаблона замены в символ нижнего регистра Преобразует символы шаблона замены в символы нижнего регистра Регулярные выражения (шаблоны) 405
26.10 Примеры задания шаблонов поиска При использовании с профаммами grep и egrep регулярные выражения берутся в кавычки. (Если шаблон содержит символ $, то нужно применять одинарные кавычки, например: 'шаблон'.) При использовании с профаммами ed, ex, sed и awk регулярные выражения обычно окружаются символами косой черты (можно применить любой разделитель). Некоторые из таких шаблонов приведены в табл. 26.8. Таблица 26.8. Примеры шаблонов поиска Шаблон bag Abag bag$ Abag$ [Bb]ag b[aeiou]g b[Aaeiou]g b.g A...$ A\. A\.[a-z][a-z] A\.[a-z]\(2\} A[A.] bugs* "слово" "* слово"* [A-Z][A-Z]* [A-ZJ+ [A-Z].* [A-Z]* [a-zA-Z] [A0-9A-Za-z] [567] Шаблоны для программ egrep u awk five I six|seven 80[23]?86 compan(yIies) Шаблоны для программ ex u vi: \<the the\> \<the\> Шаблоны для программ sed u grep: 0\{5,\) [0-9]\(3\}-[0-9]\{2\}- [0-9]\{4\] Искомая последовательность символов Строка bag bag в начале строки bag в конце строки bag как единственное слово в строке Bag или bag Вторая буква — гласная Вторая буква — согласная (или прописная, или символ, не являющийся буквой) Вторая буква — любая Любая строка, содержащая ровно три символа Любая строка, начинающаяся с точки То же, но с последующими двумя прописными буквами (например, команда программы troff) То же, только в профаммах grep и sed Любая строка, начинающаяся не с точки bug, bugs, bugss И т.п. Слово в кавычках Слово в кавычках или без кавычек Одна или несколько прописных букв То же, только в профаммах egrep и awk Прописная буква с любым (в том числе нулевым) количеством последующих символов Любое (в том числе нулевое) количество прописных букв Любая буква Любой символ (не буква и не цифра) Одна из цифр: 5, 6 или 7 Одно из слов: five, six или seven Одно из чисел: 8086, 80286 или 80386 Одно из слов: company или companies Такие слова, как theater или the Такие слова, как breathe или the Слово the Пять или больше нулей подряд Номер в системе социального страхования США (nnn-nn-nnnri) 406 Часть четвертая. Содержимое файпов
26.10 Примеры шаблонов поиска и замены Ниже, в табл. 26.9, перечислены метасимволы, использующиеся в редакторах sed и ex. (Команды редактора ех начинаются с двоеточия.) Пробел обозначен как □, а символ табуляции — как tab. Таблица 26.9. Команды поиска и замены Команда Назначение s/.*/( & )/ s/.*/mv S S.old/ /A$/d :g/A$/d /A[Dtab]*$/d :g/"[Dtab]*$/d s/DD*/D/g :%s/DD*/D/g :s/[0-9]/элемент &:/ s & sg &g %&g .,$s/Fortran/\06/g :%s/.*/\Ls/ :s/\<./\us/g :%s/yes/No/g :%s/Yes/~/g s/die or do/do or die/ s/\([Dd]ie\) or \([Dd]o\)/\2 or \1/ Повторить последнюю строку, но добавить скобки Преобразовать список слов в аргументы команды mv Удалить пустые строки То же для редактора ех Удалить пустые строки, а также строки, содержащие только символы пробела и табуляции То же для редактора ех Заменить два и более пробела одним То же для редактора ех Превратить число в текущей строке в обозначение вида элемент 5 Выполнить замену при первом совпадении То же То же, но для всех совпадений в строке То же Повторить замену глобально Заменить буквы данного слова прописными в текущей и всех последующих строках Заменить глобально все буквы строчными Заменить первую букву каждого слова в текущей строке прописной буквой (для заголовков) Заменить глобально слово yes словом No Заменить глобально слово Yes словом No (использование предыдущего шаблона замены) Поменять слова местами Поменять слова местами с использованием буфера для сохранения регистра символов — DD, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates Регулярные выражения (шаблоны) 407
27 Поиск в файлах 27.01 Различные версии команды grep Команда grep считается одним из наиболее полезных инструментов UNIX. Наверное, каждый пользователь хотел бы иметь свою, немножко отличающуюся от других версию этой команды, способную решать какую-нибудь частную задачу. (И уже само это порождает проблему. На самом деле, как говорится в документации, должна быть только одна команда grep.) Три версии этой команды поставляются с каждой UNIX-системой. Кроме того, есть еще шесть или семь бесплатно доступных версий, о которых мы здесь будем говорить, и, наверное, десятки других, которые можно найти в Сети. Приведем перечень наиболее распространенных версий команды grep с описанием их возможностей. Начнем со стандартных версий. • Старая добрая команда grep великолепна при проведении поиска с помощью регулярных выражений (параграф 27.02). • Команда egrep работает с расширенными регулярными выражениями. Она заслуженно считается самой быстрой из стандартных команд grep (параграф 27.05). • Так называемая быстрая команда grep, или fgrep (fast grep), на самом деле является самой медленной из трех. Она удобна для поиска строк, содержащих символы обратной косой черты, звездочки и. другие символы, которые обычно необходимо брать в кавычки. Команда имеет интересную возможность поиска нескольких строк (параграфы 27.06, 26.07). А теперь назовем свободно распространяемые версии. • Команда agrep (approximate -^ приблизительная) — инструмент для поиска строк, которые "более или менее" совпадают с искомой строкой. Эта очень интересная и удобная программа входит в пакет glimpse, на основе которого создается система индексирования и быстрого поиска по запросу в громадных текстах. Оба средства описаны в параграфе 27.08. • Очень быстрые версии команды grep, в частности команда egrep от организации Free Software Foundation (параграф 27.09). • Команда rcsgrep, выполняющая поиск в RCS-файлах (20.14) (параграф 27.10). Вы можете имитировать работу команды grep с помощью утилит sed, awk и perl. Указанные утилиты позволяют писать сценарии для поиска по шаблону, занимающему несколько строк (27.il), а также другие сценарии, которые имитируют работу команды grep (27.12,27.13) и показывают несколько строк перед найденным текстом и после него. (Посредством обычной команды grep можно увидеть только совпадающие строки.) - ML 408 Часть четвертая. Содержимое файлов
27.03 27.02 Поиск текста с помощью команды grep Команда grep предоставляет множество удобств для пользователей, которые часто забывают содержание своих файлов. Даже пользователи не UNIX-систем, которые посмеиваются над странноватым именем команды, хотели бы иметь утилиту, сравнимую с grep по возможностям поиска в нескольких файлах произвольного текстового шаблона, называемого регулярным выражением (26.04). Основной функцией команды grep является поиск и вывод строк, соответствующих регулярному выражению. Команда grep применяется, в частности, при необходимости узнать, как используется то или иное слово в одном или нескольких файлах. Вот как можно вывести, скажем, список строк файла ch04, содержащих либо run-time либо run time: '.:s.i4 $ grap "run[- ]time" ch04 This procedure avoids run-time errors for not assigned and a run-time error message is produced. run-time error message is produced. program aborts and a run-time error message is produced. DIMENSION statement in BASIC is executable at run time. This means that arrays can be redimensioned at run time. accessible or not open, the program aborts and a run-time Воспользовавшись этой командой, вы можете, например, найти в файле специфичные макросы программ nroff и troff (43.ы). Если файл закодирован с помощью макроса лип, то следующая команда выведет список заголовков верхнего (.HI) и второго уровней (.Н2): [..1/5.02 $ grap "A\.H[12]M ch0[12] ch01:.Hl "Introduction" ch01:.Hl "Windows, Screens, and Images" ch01:.H2 "The Standard Screen-stdscr" ch01:.H2 "Adding Characters" ch02:.HI "Introduction" ch02:.Hl "What Is Terminal Independence?" ch02:.H2 "Termcap" ch02:.H2 "Terminfo" В результате вы получите краткое содержание файла. Команда grep часто используется и как фильтр (г.оз) для обработки выходных данных других программ. Так, не все версии команды ps (38.05) позволяют выводить информацию о процессах, принадлежащих определенному пользователю, но это можно сделать путем вывода списка всех процессов и направления его команде grep: % ps -aux | grep jerry Есть несколько опций, которые обычно используются с командой grep. Опция -/ задает режим поиска с игнорированием регистра символов. Опция -с (ts.os) обеспечивает подсчет командой grep количества найденных строк. Опция -w задает поиск шаблона как отдельного слова. Например, команда grep if обнаружит совпадение с такими словами, как cliff или knife, а команда grep -w if не обнаружит подобного совпадения. Опция -/ (is.07) обеспечивает возврат только имени файла в том случае, когда команда grep обнаруживает совпадение. Ее можно использовать с целью подготовки списка файлов для других команд. Опция -у (27.вз) заставляет команду grep работать "наоборот": выводить только те строки, в которых совпадение с шаблоном не обнаружено. — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 27.03 Поиск несовпадающего текста Программы grep имеют одну очень удобную возможность: они могут отбирать и строки, не - соответствующие шаблону. Для этой цели предназначена опция -v. Поиск в файлах 409
27.04 Я использовал команду grep с опцией -v совсем недавно, работая над настоящей книгой. Наши каталоги содержали тысячи отдельных файлов, хранение которых было организовано с помощью системы RCS ро.щ, и я иногда забывал, какие из них уже проверил. Поскольку в каталоге хранилось много разнообразных файлов и с ним работали несколько человек, то простая команда Is не помогла бы. Вот почему я для вывода списка принадлежащих мне файлов использовал псевдоним с именем find. (Это была версия псевдонима find, описанная в парафафе 17.23, но с добавленными аргументами -user tim, обеспечившими отбор только моих файлов.) Верите или нет, но даже таким образом невозможно было обеспечить достаточную точность выбора из общего списка файлов. В полученном списке оказалось много временных, не интересующих меня файлов, созданных одним из наших сценариев печати. Имена всех этих файлов начинались с запятой (,), поэтому для их исключения я набрал: % find. I grep -v , Конечно, это специфичный пример. Но именно для подобных, можно сказать, специальных случаев и требуется богатая библиотека советов и приемов. В данной ситуации использовать команду grep -v, возможно, вовсе не обязательно, но когда-нибудь вы найдете ей применение. - TOR 27.04 Поиск слов К очень полезным опциям команды grep следует отнести и опцию -и\ которая обеспечивает вывод содержащих шаблон строк только в том случае, если он дает совпадение с иелым словом. Результат будет таким же, как и при использовании специальных символов \< и \> (26.04), описывающих шаблон, но набирать -w проще. (К сожалению, не все версии команды grep имеют опцию -и\) - TOR 27.05 Расширенный поиск текста с помощью команды egrep Команда egrep является одной из версий команды grep (27.02), в которой используется расширенный синтаксис регулярных выражений (26.04). Плюс (+), идущий после регулярного выражения, соответствует одному или нескольким экземплярам регулярного выражения, а знак вопроса (?) — ни одному или одному экземпляру. Кроме того, регулярные выражения могут быть вложенными, для чего используются скобки: % egrep "Lab(oratorie)?s" name.list AT&t Bell Laboratories AT&t Bell Labs Symtel Labs of Chicago Скобки вокруг части регулярного выражения и знак вопроса изменяют это выражение. Вложение позволяет исключить нежелательные совпадения. Например, слова Labors и oratories не соответствовали бы шаблону. Другой отличительной особенностью команды egrep является возможность использовать вертикальную черту (|), которая служит оператором ИЛИ при объединении двух выражений. Строки, соответствующие любому из этих выражений, будут выведены, как в следующем примере: % egrep "stdscrlcursor" ch03 into the stdscr, a character array. When stdscr is refreshed, the stdscr is refreshed. curscr. initscrO creates two windows: stdscr and curscr. 410 Часть четвертая. Содержимое фатов
27.06 Не забывайте заключать регулярное выражение в кавычки, чтобы предотвратить интерпретацию программой shell вертикальной черты как символа канала. Рассмотрим еще один пример: % egrep "Alcuin (User|Programmer)(' s)? Guide" docguide Alcuin Programmer's Guide is a thorough refer to the Alcuin User Guide Alcuin User's Guide introduces new users to Вы можете еще раз удостовериться в гибкости, обеспечиваемой синтаксисом команды egrep, которая находит соответствие либо слову User, либо слову Programmer, причем независимо от того, есть ли у них окончание 's. Пример и объяснение команды egrep приведены также в параграфе 20.08. И команда egrep, и команда fgrep (27.06) могут читать шаблон поиска из файла, если указана опция -/ (27.07). Утилита calendar (4S.M) создает, например, файл, наполненный сложными выражениями для сравнения дат. — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 27.06 Быстрая команда fgrep на самом деле таковой не является Вы наверняка слышали ранее такое высказывание: новички UNIX используют команду grep потому, что другой они не знают; опытные пользователи работают с командой fgrep, так как в документации отмечено, что она более быстрая; профессионалы используют команду egrep потому, что они уже с ней работали. Да, несмотря на то, что говорится в документации (вернее, говорилось, так как многие страницы документации по команде grep были переписаны), команда fgrep является самой медленной из трех стандартных команд grep. [Мне кажется, что название fgrep произошло не от "fast grep", а от "fixed grep", поскольку она не принимает метасимволов. — JP] Если хотите убедиться в этом, попробуйте запустить программу runtime (описана в параграфе 39.04), определяющую среднее время выполнения поиска. Ниже приведены результаты поиска строки Waldo в большом каталоге, наполненном файлами почтовых сообщений. % runtime -10 grep Waldo * AVERAGES: 4.13u 0.83s 0:04 0+203k 21+0io 19pf+0w % runtime -10 fgrep Waldo * AVERAGES: 5.19U 0.80s 0:05 0+195k 4+0io 2pf+0w % runtime -10 egrep Waldo * AVERAGES: 2.61u 0.76s 0:02 0+244k 0+0io Opf+Ow На моей рабочей станции SPARC IPC команда grep справилась с поиском за 4 секунды, fgrep — за 5 секунд, a egrep — всего за 2 секунды. Кроме того, команда egrep использовала меньше всего времени процессора. Вам интересно, как в аналогичной ситуации покажут себя некоторые другие программы поиска? Программы sed, awk и perl могут эмулировать работу команды grep: % runtime -10 sed -n '/Waldo/p' * AVERAGES: 3.64u 1.20s 0:04 0+277k 2+0io lpf+Ow % runtime -10 awk '/Waldo/p' * Поиск в файлах 411
27д7 AVERAGES: 4.86u 0.7-6S 0:05 0+279к l+0io 0pf+0w % runtime -10 perl -ne Vprintf if \(/Waldo/\) \; \' * AVERAGES: . ... 2.94u 0.69s 0:03 0+498k 28+4io 27pf+0w (Обратите внимание, что перед некоторыми символами, которые shell мог бы интерпретировать в командной строке, необходимо ставить символ обратной косой черты.) Интерпретатор perl работает быстрее всех, за исключением команды egrep, и даже редактор sed почти не уступает простой команде grep. А команда fgrep работает значительно медленнее — она проигрывает даже утилите awk\ Сказанное вовсе не означает, что команда fgrep бесполезна. Она имеет пару удобных опций. (9) | Опция -х требует, чтобы строка была точно такой же, как шаблон поиска, а опция J_ (27.07) обеспечивает чтение из файла одного или нескольких шаблонов поиска. Иногда можно }grep~ " воспользоваться и тем фактом, что командам/тер не понимает регулярных выражений, поэтому применение ее для поиска самого символа *, а также других символов регулярных выражений может упростить командную строку. Время, сэкономленное на вводе командной строки, возможно, будет достаточной компенсацией более медленной работы. - TOR 27.07 Поиск с применением нескольких шаблонов Команда egrep (27.05) позволяет выполнять поиск с использованием нескольких шаблонов, путем применения группирования и операторов ИЛИ (скобок и вертикальной черты). Но и этого не всегда достаточно. Команды egrep и fgrep (27.06) поддерживают опцию -/, которая позволяет хранить список шаблонов (фиксированных строк в случае команды fgrep) в файле, по одному шаблону на строку, и выполнять поиск. согласно всем пунктам списка с помощью одного вызова программы..При написании данной книги мы использовали такую возможность для проверки степени согласованности применяемых терминов во всех параграфах: % grep -f terna * (Точнее, мы использовали команду rcsegrep (27. ю), поскольку все параграфы создавались в системе RCS (2о.ы), но представление о том, как это делалось, вы получили.) - TOR 27.08 Система glimpse и команда agrep Glimpse — это система индексирования и поиска по запросу в текстах очень больших объемов (щ) ^ (например, во всех файлах), причем достаточно быстрая. Если вы, скажем, ищете слово something, просто наберите glimpse something, и все соответствующие строки будут glimpse выведены с указанием имени файла. Прежде чем использовать систему glimpse, вы должны проиндексировать файлы, запустив программу glimpseindex. Запускать ее можно периодически, например каждую ночь, с помощью системы сгоп (40.12). Правда, те файлы, которые появились после последнего выполнения программы glimpseindex, при поиске будут пропущены. За исключением этого неудобства (которого в любой индексной системе избежать невозможно), система glimpse превосходна — особенно благодаря тому, что, как правило, работает достаточно быстро. Скорость работы программы зависит от размера индексного файла: больший индексный файл обеспечивает более быстрый поиск. Но даже при минимальном размере индексного файла я могу просмотреть весь свой 70-мегабайтовый почтовый архив на довольно медленной рабочей станции менее чем за 30 секунд. При наличии более быстрых процессора и дисков поиск мог бы осуществляться еще быстрее. Правда, имеется один недостаток при работе с 412 Часть четвертая. Содержимое файлов
27.08 шаблонами, соответствующими содержимому многих файлов, поиск в которых может занять много времени: система glimpse выводит предупреждение и спрашивает, хотите ли вы продолжить поиск. (После того как система glimpse проверит свой индекс на возможные совпадения, для проведения поиска в потенциально подходящих файлах запускается команда agrep, которая проверяет совпадения и выдает точно соответствующие записи.) Команда agrep — Одно из самых полезных дополнений в семействе grep. Она не только относится к самым быстрым командам этого семейства, но и обладает уникальной возможностью приблизительного поиска. Эта команда ориентирована, скорее, на работу с записями, а не со строками. Вызывает команду agrep система glimpse, однако ее можно использовать и без указанной системы. Причем эта команда имеет три важные особенности, не поддерживаемые семейством grep. 1. Команда agrep выполняет поиск приблизительных совпадений с определяемой пользователем точностью. Так, команда % agrep -2 homogenos foo будет искать слово homogeneous, а также любое другое слово, которое можно получить из homogenos с помощью двух замен, вставок или удалений. Команда % agrep -В homogenos foo выведет сообщение типа best match has 2 errors, there are 5 matches, output them? (y/n) 2. Команда agrep ориентирована на работу с записями, а не со строками. По умолчанию запись — это строка, но с помощью опции -d можно определить последовательность символов, которая будет использоваться в качестве разделителя записей. Например, команда % agrep -d lAFrom < 'pizza1 mbox выводит все почтовые сообщения (из) (разделены строками, начинающимися со слова From и пробела), хранящиеся в файле mbox и содержащие ключевое слово pizza. Команда % agrep -d '$$' шаблон foo выводит все абзацы (разделены пустыми строками), содержащие указанный шаблон. 3. Команда agrep допускает задание нескольких шаблонов, объединенных операторами И и ИЛИ в логические запросы. В частности, команда % agrep -d lAFrom ' 'burger,pizza' mbox выводит все почтовые сообщения, содержащие, по крайней мере, одно из ключевых слов (запятая соответствует оператору ИЛИ). Команда % agrep -d ,AFrom ' 'burger;pizza' mbox выводит все почтовые сообщения, содержащие оба ключевых слова. Объединив все перечисленные возможности, можно написать примерно такой запрос: % agrep -d '$$' -2 <САСМ>,Дв!Рор;Сигг1си1ит;<19в[5-91>' bib который выводит все абзацы, содержащие ссылки на статьи указанного автора в САСМ за период с 1985 по 1989 год, включающие, в свою очередь, слово Curriculum. Допускается наличие двух ошибок, но их не должно быть ни в слове САСМ, ни в обозначении года. (Угловые скобки < и > предотвращают возникновение ошибок при сравнении с шаблонами, записанными между ними.) Другие юзможности команды agrep связаны с поиском, осуществляемым с применением регулярных выражений, широким использованием метасимволов, ограничением возможных ошибок только вставками или заменами либо же их сочетанием (допустимы, например, две замены и три вставки), выделением точных и приблизительных частей запроса и т.д. Поиск в фьйпах 413
27.09 Адрес электронной почты glimpse-request@cs.arizona.edu следует добавить в список рассылки системы glimpse. По адресу glimpse@cs.arizona.edu можно посылать сообщения об ошибках, задавать вопросы, обсуждать приемы использования системы glimpse и т.д. (Это не очень активный список рассылки, с его помощью в основном распространяются объявления.) - JP, SW, UM 27.09 Новые команды grep намного быстрее Многие новые общедоступные версии команды grep, в частности agrep (27.0s) и GNU egrep, используют более быстрый алгоритм поиска Бойера-Мура (Воуег-Мооге). Обе версии команды имеются на компакт-диске.* В параграфе 27.06 мы использовали программу runtime (39.04) для сравнения времени выполнения задания командами fgrep, egrep и grep. Сейчас же мы воспользуемся данной программой для определения среднего времени, необходимого для поиска строки Waldo в большом каталоге с файлами. Сначала найдем результат с помощью команды agrep: % runtime -10 agrep Waldo * AVERAGES: 0.48u 0.73s 0:01 0+261k 0+0io Opf+Ow А теперь — с помощью команды egrep: % runtime -10 egrep Waldo * AVERAGES: 0.62u 0.62s 0:01 0+242k 0+0io Opf+Ow Сравним эти числа с полученными в параграфе 27.06. Вот что, например, нам дало использование обычной команды grep: % runtime -10 grep Waldo * AVERAGES: 4.13u 0.83s 0:04 0+203k 21+01o 19pf+0w Эта команда на поиск во всех файлах потратила целых 4 секунды, в то время как agrep и GNU egrep уложились в 1 секунду. - TOR, LM 27.10 Поиск в RCS-файлах с помощью сценария rcsgrep Сохранение в системе RCS (2о.ы) многих версий файла позволяет сэкономить дисковое пространство. Но как осуществить одновременный поиск во всех этих файлах? Можно, конечно, извлечь все файлы, затем запустить команду grep, однако после завершения поиска все файлы необходимо будет удалить. Можно также искать в самих RCS-файлах, с помощью команд типа grep foo RCS/*, v, но при этом будет выведено много "мусора" из предыдущих версий, служебных записей и другого текста, который не является последней версией вашего файла. В данном параграфе предлагается два решения указанной проблемы. Команды rcsgrep, rcsegrep и rcsfgrep _,- Сценарий rcsgrep, а также две ссылки на него, с именами rcsegrep и rcsfgrep, запускают комйнды grep, egrep (27.05) и fgrep (27.06) для поиска во всех файлах RCS-каталога. (Можно также выбрать файлы, в которых нужно искать.) Сценарий проверяет имя, по которому он был вызван, чтобы решить, какую команду — grep, egrep или fgrep — он будет выполнять. Затем он извлекает каждый файл и передает его по Исполняемый модуль GNU egrep также инсталлируется под именем grep, поэтому будьте внимательны! 414 Часть четвертая. Содержимое файяов
27.11 каналу той версии команды grep, которую вы выбрали. Вывод выглядит точно так же, как вывод команды grep, хотя по умолчанию при этом выводятся и сообщения команды со (вывод таких сообщений подавляет опция -s). По умолчанию сценарий rcsgrep ведет поиск в последней версии каждого файла. При наличии опции -а поиск будет вестись во всех имеющихся версиях каждого файла. Это особенно удобно, когда необходимо выяснить, что изменилось в определенном месте, с тем чтобы узнать, в каких версиях был искомый текст, удаленный недавно. (Сценарий rcsgrep использует команду rcsrevs (20.15), чтобы реализовать опцию -а.) Опции -е, -/и -/ команды grep могут работать в сценарии лишь при условии особого способа их использования. (Например, после опций -е и -/ всегда идет аргумент. Сценарий должен правильно передать и опцию, и аргумент.) Сценарий пересылает любые другие введенные вами опции команде grep. Ваша версия команды grep может иметь другие опции, которые также требуют специальной обработки в сценарии. Но текст сценария должен быть соответствующим образом отредактирован. Сценарий rcsgrep можно инсталлировать с компакт-диска или из сетевого архива (52.07). При получении сценария из архива с помощью команды tar инсталлируйте rcsgrep и две ссылки на него — rcsegrep и rcsfgrep, а также команду rcsrevs. Сценарий rcsegrep.fast Для проведения поиска в RCS-файле сценарий rcsgrep и его "братья" запускают несколько UNIX-процессов: со, grep, sed и некоторые другие. На запуск и выполнение каждого процесса тратится определенное время. Если в вашем каталоге хранятся сотни RSC-файлов (как в нашем каталоге, используемом для написания этой книги), то поиск, скорее всего, займет много времени. Вы можете уменьшить количество используемых процессов, переписав сценарий rcsgrep на языке Perl. Интерпретатор Perl имеет встроенные функциональные возможности команд grep, sed и других, поэтому все, что нужно было бы сделать, — это запустить сотни процессов со, которые все равно замедляли бы работу. Решение, к которому я пришел, состоит в использовании одного процесса и реализуется в сценарии gawk (зз.п). Вместо того чтобы применять RCS-команду со для извлечения последней версии файла, сценарий rcsegrep.fast напрямую читает каждый RCS-файл. (Формат RCS-файла описан на странице документации rcsfile(5).) RCS-файл содержит последнюю версию своего рабочего файла в виде обычного текста с одним отличием от такового: каждый символ @ заменен символами @@. Сценарий rcsegrep.fast просматривает RCS-файл, пока не обнаружит начало текста. Затем он применяет к каждой строке регулярное выражение, подобное используемым в команде egrep. Соответствующие строки с именем файла в начале направляются на стандартный вывод; опция -и обеспечивает добавление номера строки после имени файла. Сценарий rcsegrep.fast относится к ненадежным решениям, поскольку обращается к RCS-фай- лу, не используя средств системы RCS. Может случиться, что он не станет работать с некоторыми версиями системы RCS или же обнаружатся другие мои программистские промахи. Но у нас этот сценарий работал прекрасно. Он намного быстрее сценария rcsgrep и ему подобных. Я бы рекомендовал использовать сценарий rcsegrep.fast при необходимости отслеживать производимые изменения среди множества RCS-файлов. - JP 27.11 Использование редактора sed для многострочного поиска [Один из недостатков семейства команд grep — их ориентация на строки. Эти команды читают файл построчно, поэтому не могут находить последовательности символов (например, фразы), расположенные на двух строках. Команда agrep (27.0s) может вести многострочный поиск. Одним из достоинств сценария cgrep.sed является то, что анализ его содержимого позволяет понять, как можно обрабатывать многострочные последовательности в редакторе sed. Этот сценарий можно приспособить для выполнения других задач, а не только для поиска. — JPJ Поиск в файлах 415
27.12 Вы, по-видимому, будете удивлены, узнав, что путем использования редактора.serf можно создать неплохой аналог команды grep. Эта serf-версия команды cgrep основана на том же методе, что и ее Perl-версия, описанная в параграфе 27.13. Так, команда $ сдхер -10 system main.с находит все строки, содержащие слово system, в файле main.c и выводит по 10 дополнительных строк контекста до и после каждого совпадения. (Опция -контекст должна задавать не менее 1 строки, а по умолчанию выводятся 2 строки.) Эта команда отличается от Perl-версии тем, что если в пределах одного контекста встречаются два совпадения, то строки выводятся одним большим блоком, а не несколькими малыми. Каждый новый блок контекста предваряется номером строки, в которой отмечено совпадение. Этот сценарий способен также искать последовательности символов, размещенные в нескольких строках. Скажем, команда $ cgrep -3 "awk.*perl" находит все экземпляры слова awk, после которого следует слово perl, в пределах трех следующих строк. Шаблон может быть любым простым регулярным выражением (26.04), правда, с одним существенным отличием: поскольку текст можно сравнивать в разных строках, то вместо метасимволов * и $ необходимо использовать символы \п. Работа описанного сценария объясняется в параграфе 34.17. - GU 27.12 Создание пользовательских команд в стиле grep с помощью языка Perl Все утилиты типа grep выполняют одну и ту же работу, несколько отличаясь друг от друга тем, что поиск ведется в части файла или во всем файле, а найденная последовательность символов выводится с различным количеством окружающего контекста. Чем больше вы работаете в UNIX, тем чаще вам приходится решать задачи поиска, причем решить их все не может ни одна утилита UNIX (отсюда необходимость в различных версиях команды grep, о которых шла речь выше). У вас начинают скапливаться программы на С, сценарии awk, сценарии shell, выполняющие разнообразные задачи, и вы начинаете искать одну утилиту, которая бы выполняла всю работу — лишь при этом условии не нужно будет тратить дисковое пространство на хранение многочисленных программ. Такой утилитой может служить интерпретатор языка Perl (37.01) (Practical Extraction and Report Language — практический язык для создания отчетов и выборок). Согласно сопроводительной документации, Perl, разработанный Ларри Уоллом, является "интерпретируемым языком, оптимизированным для просмотра любых текстовых файлов, извлечения из них информации и вывода на основании этой информации отчетов". Если интерпретатор perl в вашей системе еще не инсталлирован, можете взять его с компакт-диска. Чтобы найти заданную последовательность символов, предположим, в заголовке сообщения Usenet, выполните команду perl -ne 'exit if (/A$/); print if (/шаблон/);' файл [Почтовые сообщения и сообщения Usenet о.зз) всегда содержат пустую строку (в регулярном выражении обозначена символами А$), отделяющую заголовок от самого сообщения. — TOR] Чтобы выполнить поиск по шаблону и вывести абзацы, в которых обнаружены совпадения, введите команду perl -ne '$/ = "\n\n"; print if {/шаблон/); ' файл [Здесь предполагается, что абзацы разделены двумя символами новой строки, т.е. пустой строкой. Вы можете подправить этот сценарий, сделав его пригодным для работы с документами troffa ТЕХ, в которых абзацы выделяются специальным кодом. — TOR] 416 Часть четвертая. Содержимое файлов
27.13 Преимущества языка Perl проявляются не только при поиске в файлах. Perl содержит все функциональные возможности программ sed, awk, grep, find, а также других утилит UNIX. Более того, Perl-программа, реализующая какое-то задание, которое раньше выполнялось с помощью одной или нескольких из этих утилит, обычно работает быстрее, и читать эту программу легче. [Я согласен, что Perl-программы, как правило, функционируют быстрее, чем пакет из отдельных утилит UNIX, соединенных посредством каналов и временных файлов. Такие программы по быстродействию превосходят и отдельные утилиты. Но по своему опыту знаю, что редактор sed почти всегда работает быстрее, чем Perl. Это обстоятельство частично можно объяснить тем, что я использую "медленный" диск, и 40-килобайтовый двоичный файл sed загружается быстрее, чем 700-килобайтовый файл интерпретатора Perl 5. Вы можете провести собственное тестирование, и в третьем издании этой книги я, если возникнет такая необходимость, принесу свои извинения Джонатану. :-) — JP] - ПК 27.13 Еще несколько программ типа grep, написанных на Perl [Параграф 27.12, думаю, убедил вас в том, что написать пользовательскую программу поиска на Perl достаточно легко, но пока вы выучите язык, то забудете, для чего он вам понадобился. В этом параграфе рассказано еще о нескольких пользовательских командах grep, написанных на Perl. Правда, сами тексты сценариев здесь не приведены, а только даны инструкции по их использованию. Если эти сценарии вам понравятся, вы сможете найти их на компакт-диске и в сетевом архиве. — TOR] Программа tgrep производит поиск только в текстовых файлах. Она удобна для работы с каталогами, в которых перемешаны двоичные и текстовые файлы, особенно когда имени файла недостаточно для определения его типа. Команда tgrep имеет опцию -/, которая tgrep заставляет ее выводить список файлов, содержащих заданную последовательность символов, а не список строк. Программа pipegrep производит поиск в выходном потоке цепочки команд. Трудность применения для этой цели обычной команды grep состоит в невозможности проследить за тем, какой файл был обработан. Программа pipegrep выводит имена выполняемых команд вместе с именами файлов. Команда, являющаяся единственным аргументом, будет выполнена по одному разу для каждого файла из списка. Если в любом месте командной строки поместить { }, то здесь же будет подставлено имя файла. В противном случае имя файла добавляется в конец командной строки. Эта программа имеет одну опцию, -/, которая заставляет ее выводить только имена файлов, содержащих заданную последовательность символоа Например [пт — это утилита для программистов, которая выводит список имен. — JP\: $ cd /usr/lib $ pipegrep 'sys_nerr' nm lib*.a nm /usr/lib/libXll.a |: U_sys_nerr nm /usr/lib/libXaw.a |: U_sys_nerr nm /usr/lib/libXaw.a |: U_sys_nerr nm /usr/lib/libXc.a |: U_sys_nerr 0 ез Программа cgrep осуществляет поиск в указанных файлах и выводит соответствующую строку вместе с окружающим ее контекстом. Этот сценарий позволяет задавать количество строк контекста, которое может быть больше или меньше выбираемого по умолчанию. Например, cgrep команда $ cgrep -3 шаблон файла выводит по три строки до и после искомой. Вывод каждого случая совпадения отделяется от следующего короткой строкой ( ). — LW, RS, ТС, из книги Programming Perl издательства O'Reilly & Associates Поиск в файпах 417 14 9-171
27.14 27.14 Комбинированный поиск Вы, возможно, помните, что поиск строк, содержащих слово это или то, можно вести с использованием метасимвола | команды egrep (27.0s/. egrep 'это'|'то' файлы А как можно найти и это, и то? Обычные регулярные выражения не поддерживают оператор И, поскольку он нарушает правило, согласно которому шаблон должен соответствовать одной последовательной строке текста. Однако есть команда agrep (27.0s), которая нарушает все правила. Если она у вас имеется, то просто введите agrep 'cat;dog;bird1 файлы Если же у вас нет команды agrep, то текст, как обычно, можно отфильтровать с помощью нескольких команд grep таким образом, чтобы дальше по каналу передавались только те строки, которые содержат все ключевые слова: grep cat файлы | grep dog | grep bird А можно ли это сделать посредством одной команды? Ближе всего к решению этой задачи можно подойти с помощью команды grep, использовав такую идею: grep 'cat.*dog.*bird' файлы которая, правда, имеет два ограничения: слова должны появляться в указанном порядке и не должны перекрываться. (Первое ограничение можно преодолеть посредством команды egrep ' cat. *dog I dog. *cat', но этот трюк невозможно применить по отношению более чем к двум словам.) Конечно же, проблему можно решить, если выйти за пределы семейства grep и воспользоваться более мощными средствами. Вот как выполняется построчный поиск с помощью оператора И при использовании программ sed, awk* или perl: sed '/cat/!d; /dog/!d; /bird/Id" файлы awk '/cat/ &£ /dog/ &£ /bird/' файлы perl -ne 'print if /cat/ Si /dog/ &S /bird/' файлы А что если нужно найти абзацы, в которых содержатся все три указанных слова? В таком случае необходимо включить режим работы с абзацами, указав в программе awk команду RS="" или задав в perl опцию -00: awk '/cat/ &£ /dog/ && /bird/ (print $0 ORS}' RS= файлы perl -nOOe 'print "$_\n" if /cat/ && /dog/ &S /bird/' файлы А если нужно вывести список файлов, которые содержат все эти слова? Пожалуйста: perl легко обработает все файлы, если у вас есть достаточно памяти и используется опция -0, устанавливающая такой разделитель записей, который не может появиться в файле (например, символ NUL): perl -InOe 'print $ARGV if /cat/ iS /dog/ &S /bird/' файлы (Обратите внимание, что по мере усложнения задач и менее мощные команды "выбывают из игры".) Описанная выше методика, основанная на использовании команды grep как фильтра, подходит для решения и этой проблемы. Вам лишь следует добавить опцию -Iqs.07) и команду xargs (9.21), чтобы в канал выводились не строки, а имена файлов: grep -1 cat файлы | xargs grep -1 dog I xargs grep -1 bird (Команда xargs, как правило, служит связующим звеном, когда одна программа выводит данные, которые используются другой командой в качестве аргументов командной строки.) - GU * Некоторые версии программы iiawk требуют наличия символов $0- перед каждым шаблоном. 418 Часть четвертая. Содержимое файлов
27.16 27.15 Сужение диапазона поиска Если вам необходимо просмотреть длинный файл в поисках определенного слова или имени, выполнить программу наподобие Is -I и отфильтровать некоторые строки, попробуйте воспользоваться следующим быстрым способом сужения диапазона поиска. Предположим, что ваш файл номеров телефонов содержит 20000 примерно таких строк: Smith, Nancy:MFG:50 Park Place:Huntsville: (205)234-5678 и вы хотите найти кого-то по имени Нэнси (Nancy). Чем больший объем информации вы имеете, тем проще определить, какая из всех Нэнси вам нужна: % grep Nancy phones Чтобы убрать ненужные строки, воспользуйтесь механизмом подстановки команд из перечня (П.02) интерпретатора С shell и редактором sed (34.24). Допустим, что треть всех Нэнси живет в Хантсвилле, а вы точно знаете, что интересующая вас Нэнси там не работает: % !! I sed -e /Huntsville/d grep Nancy phones | sed -e /Huntsville/d Интерпретатор shell показывает выполняемую команду — предыдущую команду (! !), соединенную каналом с командой sed, которая удаляет из вывода команды grep строки, содержащие СЛОВО Huntsville: Отлично! К тому же вы знаете, что Нэнси не работает в группах MPG и SLS, поэтому смело можете удалить еще несколько строк: I !! -е /MPG/d /SLS/d grep Nancy phones | sed -e /Huntsville/d -e /MPG/d -e /SLS/d Продолжайте использовать операторы ! !, повторяя предыдущие команды и добавляя новые выражения для команды sed, до тех пор, пока список не станет достаточно коротким. Данный прием можно применить и по отношению к другим командам. Когда вы, например, вылавливаете ошибки в выводе команды uulog о.зз), то необходимо пропустить строки, содержащие слова SUCCEEDED и ОК: % uulog | sed -e /SUCCEEDED/d -e /OK/d Если шаблон наряду с буквами и цифрами содержит другие символы, вам необходимо понимать механизм защиты специальных символов, применяемый в интерпретаторе shell (а.м), и регулярные выражения (26М) редактора sed. Однако в большинстве случаев этот простой и быстрый способ дает вполне удовлетворительные результаты. - JP 27.16 Поиск, осуществляемый без учета регистра Следующий совет можно считать самым простым в нашей книге. Во многих версиях UNIX команда egrep не поддерживает опцию -/, которая обеспечивает поиск текста без учета регистра. Возможность выполнения такого поиска чрезвычайно важна, особенно для писателей. Ведь угадать, пишется ли данное слово с большой буквы, практически невозможно. Чтобы организовать с помощью команды egrep поиск текста без учета регистра, достаточно исключить все буквы, которые могут быть прописными. Так, вместо слова Example следует просто искать слово xample. Если буква, которая может быть прописной, находится посредине слова, в шаблоне пропускаемую букву можно заменить точкой (она будет соответствовать одному символу). Конечно, это можно сделать "правильно", с помощью команды % egrep '[eE]xample' * однако наш вариант проще. Этот совет, очевидно, касается не только команды egrep. Его можно применить по отношению к любой утилите, в которой при поиске учитывается регистр, например к утилите more. - ML Поиск в файлах 14* 419
27.17 27.17 Поиск символа, находящегося в определенной позиции Предлагаем идею для реализации поиска строк, которые содержат заданный символ в определенной позиции: % awk 'substr($0,n,l) = "с"' файл где с — искомый символ, ал — номер интересующей нас позиции. В каком случае это может понадобиться? В первую очередь при обработке файлов, имеющих жестко заданную структуру. Например, у вас может быть список телефонов с символами # во второй позиции для "разговорных" номеров, $ — для модемов и % — для факсов. Сценарий, выполняющий поиск телефонных номеров, может использовать команду утилиты awk, подобную этой, чтобы предупредить ваш "разговор" с факсом. Если данные содержат символы табуляции, то позиции могут иметь не тот номер, о котором вы подумали. В таком случае примените к файлу команду expand (пм), а затем передайте данные по каналу команде awk. - JP, ML 27.18 Быстрый поиск и проверка правописания с помощью программы look Время от времени создаются новые команды типа grep. Общедоступные архивы программного обеспечения содержат таковых немало. Одной из самых быстрых среди появившихся в последнее время программ поиска является программа look, использующая очень быстрый двоичный метод поиска. Однако программа look не решит всех ваших проблем, так как работает только с отсортированными файлами (X.oi). Если у вас есть большой файл или база данных, которые можно отсортировать, то поиск в них с помощью программы look сэкономит много времени. Например, чтобы найти все строки, начинающиеся со слова Alpha, задайте команду % look Alpha файл Alpha particle Е Alphanumeric Программу look можно также использовать для проверки правописания или поиска связанных слов (см. параграф 29.3). Если у вас нет этой программы, инсталлируйте ее с компакт-диска. look _ jp 27.19 Поиск слов в двоичных файлах При попытке вывести двоичные файлы ($зм) на экран с помощью, скажем, команды cat -v (25.07) вы увидите множество непечатаемых символов. Причем среди них могут затеряться слова и строки, которые имеют определенный смысл. Например, если код защищен авторским правом, то обычно эта информация должна присутствовать в дюичном файле. Путевые имена специальных файлов также могут отображаться. Если вас интересует, какая программа вывела, сообщение об ошибке, примените к двоичному файлу команду strings и поищите текст сообщения об ошибке. Некоторые версии команды strings работают хорошо, выдавая только полезную информацию, а некоторые могут выводить также и много "мусора". Но это не беда. Передайте вывод по каналу какой-нибудь программе постраничного вывода (25.ш,25.мх или команде grep (27.02), переадресуйте его в файл — и вы избавитесь от нежелательных данных. Приведем сокращенный пример вывода команды strings, полученный в SunOS: % strings /bin/write @(#)write.c 1.10 88/05/10 SMI Usage: write user [ttyname] write: Can't find your tty Message from %s@%s on %s at %d:%02d ... Write failed (%s logged out?) ((((( DDDDDDDDDD 420 Часть четвертая. Содержимое файлов
27.20 Первая строка поступает от системы SCCS (Ж12). Здесь виден номер версии, дата последней модификации или выпуска и т.д. Символы %s, %d и %02d являются операторами форматирования, которые функция /?лл(ДЗ). заменит такими значениями, как имя пользователя, имя сервера, время последней модификации (часы и минуты). По умолчанию команда strings читает не весь двоичный файл,, а только его загруженные разделы. Опция - (дефис) заставляет команду strings просматривать весь файл. Еще одной полезной опцией является -л, где л — число, задающее минимальную длину выводимой строки. Причем установка небольшого значения позволяет отбросить ненужную информацию, но при этом можно потерять то, что ищешь. Команда od -sn делает примерно то же: находит строки длиной не менее л символов, заканчивающиеся нулевым кодом. - JP 27.20 Команда grep, выделяющая найденные слова Замечали ли вы, что при поиске нужного слова в выводе команды grep обнаружить это слово в каждой строке трудно? Предположим, нам нужно найти почтовые сообщения, в которых что-то говорится о языке программирования Perl. Но при поиске в файле с помощью команды grep большинство строк кажутся бесполезными: % grep perl -/Mali/save >. and some of it wouldn't compile properly. I wonder if Subject: install script, for perl scripts perl itself is installed? > run but dies with s read error because it isn't properly > if I "can get it installed properly on another machine I > run but dies with a read error because it isn't properly > if I can get it installed properly on another machine I Поэтому предлагаю познакомиться с программой, которая, как сказано в документации, "тривиальна, но умна". Сценарий hgrep запускает команду grep и выделяет найденную строку, чтобы ее легче можно было заметить. ■ % bgrep perl -/Mail/save > and some of it wouldn't compile ргоф^Ду- I wonder if Subject: install script, for З^Д scripts jjjJH itself is installed? > run but dies with s read error because it isn't pn > if I can get it installed ргоЩЗДу on another machine - > run but dies. with.a read error because it isn't pr > if I can get it installed proQ^By on another machine I Теперь мы знаем, почему большая часть вывода кажется бесполезной: потому что таковой она и есть! К счастью, сценарий hgrep является всего лишь оболочкой. Он просто передает все свои аргументы команде grep. Сценарий, конечно же, принимает все опции этой команды, и вы можете воспользоваться опцией ^w (27.04) для удаления из вывода всего лишнего: % bgrep -w perl -/Mail/save . Subject: install script, for jjjJH scripts itself is installed? - LM Поиск в файлах . 421
28 Сравнение файлов 28.01 Установление различий с помощью команды diff И Команда diff отображает различающиеся строки, обнаруженные при сравнении двух файлов (На компакт-диске есть также GNU-версия этой программы.) Она выводит сообщение, в котором для описания происшедших в строках изменений используется система записи, применяемая в jiff редакторе ed (а — добавлено, с — изменено, d — удалено). Затем следуют сами строки. Символ < предшествует строкам из первого файла, а символ > — строкам из второго файла. Попытаюсь объяснить вывод команды diff, создав для этой цели специальный пример. Просмотрим содержимое трех файлов: testl apples oranges walnuts test2 apples oranges grapes test3 oranges walnuts chestnuts Применив команду diff к файлам test] и test2, мы получим такой вывод: $ diff testl test2 ЗсЗ < walnuts > grapes Команда diff выводит только те строки из этих файлов, которые отличаются друг от друга. Вы не поймете этот отчет, если не вспомните, что указанная команда описывает изменения, которые необходимо внести в первый файл, чтобы сделать его аналогичным второму. Из представленного выше отчета видно, что изменилась только третья строка, где вместо слова walnuts содержится слово oranges. Это становится еще более очевидным при использовании опции -е, обеспечивающей вывод сценария, который можно передать строковому редактору ed. (Для сохранения этого сценария в файле необходимо переадресовать стандартный вывод (is.oi).) $ diff -e testl test2 Зс grapes После выполнения такого сценария файл testl станет таким же, как 1est2. (В параграфе 28.09 объясняется, как можно "заставить" редактор ed выполнить этот сценарий.) При сравнении файлов testl и test3 отличий будет обнаружено больше: $ diff testl test3 IdO 422 Часть четвертая. Содержимое файлов
28.02 < apples ЗаЗ > chestnuts Чтобы сделать файл testl аналогичным файлу test3, необходимо удалить его первую строку (apples) и после третьей строки добавить третью строку файла test3. Опять же, сказанное станет более наглядным на примере сценария редактирования, созданном с помощью опции -е. Заметим, что сценарий представляет редактируемые строки в обратном порядке, в противном случае изменение первой строки привело бы к изменению нумерации всех последующих строк. $ diff -e testl test3 За chestnuts Id Вы спросите, зачем все это нужно? Приведем пример. При работе над документом, как правило, создается копия файла и редактируется именно она, а не оригинал. В частности, так обычно делается, когда не сам автор документа, а кто-либо иной вносит исправления с печатной копии. Для сравнения двух версий документа применяется команда diff'. Автор может воспользоваться этой командой для сравнения исправленной копии с оригиналом. $ diff brochure brochure.edits 49c43,44 < environments for program development and communications, > environments for multiprocessing, program development > and communications, programmers 56c51 < offering even more power and productivity for commericial > offering even more power and productivity for commercial 76c69 < Languages such as FORTRAN, COBOL, Pascal, and С can be > Additional languages such as FORTRAN, COBOL, Pascal, and Использование команды diff подобным образом является для автора простым способом проверки изменений без чтения всего документа. Путем переадресации вывода команды diff в файл можно сохранить изменения, внесенные в любой документ. По сути, такой же метод применяется в системах SCCS и RCS ао.п) для слежения за множественными версиями исходного кода и документов. — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 28.02 Сравнение трех версий файлов с помощью команды diff3 И Команду diff3 можно использовать для отслеживания различий между тремя файлами. (На компакт-диске имеется также GNU-версия этой программы.) Воспользуемся тремя файлами, взятыми из параграфа 28.01: testl apples oranges walnuts test2 apples oranges grapes test3 oranges walnuts chestnuts Сравнение фвйлов 423
28.03 Для каждого набора отличий команда diff3 выводит строку ==== с последующими цифрами 1, 2 или 3, указывающими, какой файл отличается. Если не указан никакой номер, значит, все три файла разные. Далее различия всех файлов описываются с использованием записей в стиле редактора ed (2S.01). $ diff3 testl t«st2 test3 1:1c 2:1c apples 3:0a 1:3c walnuts 2:3c grapes 3:2,3c walnuts chestnuts С помощью вывода команды diff3 легко следить за содержимым файлов, однако понять предлагаемые рекомендации несколько сложнее. В соответствии с первой частью выведенного текста (после ====3), для согласования этих файлов необходимо в начале третьего файла добавить apples (3:0а). Во второй части текста сказано, что необходимо заменить строку 3 второго файла строкой 3 первого файла, а также заменить строки 2 и 3 третьего файла. Команда diff3 имеет также опцию -е, предназначенную для создания- сценария редактора ed. Эта опция работает несколько по-иному, чем можно было бы предположить. В первую очередь она создает сценарий для построения первого файла на основании второго и третьего. $ diff3 -• teetl t*st2 t*et3 Зс walnuts chestnuts Id w q Если второй и третий файлы поменять местами, то будет создан другой сценарий: $ diff3 -в testl t*et3 test2 Зс grapes w q Как вы, юзможно, догадались, это почти такой же выюд, какой был у команды diff при обработке первого и третьего файлов. (Единственное отличие вызвано довольно странной несогласованностью между командами diff и diff3. Версия команды diff3 в System V создает erf-сценарий, завершающийся командами сохранения отредактированной версии файла. В Berkeley UNIX команда diff3, а также обе версии команды diff требуют, чтобы им были предоставлены аргументы w и q. Здесь мы рассказывали о версии System V.) — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 28.03 Вывод контекста командой diff В примерах использования команд diff в параграфах 28,01 и 28,02 представлены компактные форматы вывода, отражающие только различия двух файлов. Но во многих случаях был бы очень полезен вывод различий вместе с контекстом. Контекстные команды, diff выводят 424 Часть четвертая. Содержимое файлов
28.03 измененные строки, окруженные прилегающими к ним строками. (Это может быть неудобным при чтении списка на терминале, когда есть много измененных, но очень похожих строк: контекст может представлять собой довольно большое количество, строк до и после измененной строки. В подобных случаях более удобным, надо полагать, является компактный формат вывода.) Во многих версиях команды «//^(включая GNU-версию, записанную на компакт-диске) опция -с обеспечивает вывод контекста, окружающего измененную строку. Сама по себе эта опция задает представление по три строки до и после той, в которой обнаружено изменение. Ниже в качестве примера приведен файл на языке C++ до и после нескольких модификаций. Опция -с2 обеспечивает вывод двух строк контекста: % diff -c2 include.h. *** include.h.orig include.h *************** *** 45,52 **** private: Node *head; ! Node *last; orig include.h Fri Aug 23 22: 17:00 1996 Fri Aug 23 23:31:30 1996 // first member in list // last member in list public: ! void void void — 45,52 - private: ■ Node ! Node load (void) ; figure_^tax (Taxer tax_obj), summarize(void); // insert data into list *head; *tail; // do calculations // first member in list // last member in list public: ! void load(char *infile); void figure_tax(Taxer tax_ob: void summarize(void); *************** *** 77,84 **** int tax; .int percent; int boundary; } tax_array[TAX_TABLE_RECORDS]; // read data, insert into .list // do calculations public: double give_tax(double gross_pay); }; — 77,85 int tax; int percent; } tax_array[TAX_TABLE_RECORDS]; // search array, get tax public: + - Taxer(void) + -Taxer(void); // constructor //■ destructor double give^tax (double gross_pay); // search array, get tax }; Листинг начинается двумя именами файлов и датами их последней модификации (временными метками). Первое имя (здесь — include.h.orig) помечено тремя звездочками (***), а второе имя — тремя дефисами (—). Данные маркеры отмечают начало фрагмента соответствующего файла в списке отличий, который приведен ниже. Каждая измененная часть текста начинается с длинной строки звездочек. Далее указываются номера (окружены группой звездочек) тех строк из перюго файла, в промежутке между Сравнение файлов 425
28.04 которыми обнаружены отличия. После строк первого файла указываются номера строк второго файла (помечены тремя дефисами), а затем приводятся сами эти строки. Строки, измененные в обоих файлах, помечены с левого края восклицательным знаком (!). Так, мы видим, что в диапазоне от 45-й по 52-ую строку изменения были произведены в двух случаях. В файле include.h.orig есть строка Node *last;, которая в файле include.h после частичного изменения превратилась в Node *tail;. Кроме того, изменились и строки, начинающиеся с void load. Другие строки остались без изменений. Следующие различия были найдены в строках 77-84 файла include.h.orig и строках 77-85 файла include.h. Знак - у левого края показывает, что строка int boundary была удалена из файла include.h.orig и во втором файле ее нет. В файле include.h есть две новые строки, помеченные с левого края знаком +. Контекстные отчеты интересны не только для чтения. Программа patch (ззщ может читать листинги контекстных команд diffw использовать их для автоматического обновления файлов. Например, если бы у меня был файл include.h.orig и кто-то прислал мне приведенный выше листинг (так называемую заплату), то программа patch из исходного файла и заплаты могла бы создать файл include.h- Преимущество контекстного вывода команды ^состоит в том, что он позволяет программе patch находить измененные участки, даже если они немного переместились. - JP 28.04 Цва файла рядом: команда sdiff Немного поработав с командой diff, нетрудно научиться читать ее вывод. Однако иногда W 1 бывает проще рассмотреть файлы, расположенные рядом. Обеспечить такую возможность может команда sdiff. (На компакт-диске также имеется GNU-версия этой команды.) Между ~sdjff~ текстами двух файлов она выводит символ < для строк, присутствующих только в первом файле, > — для строк, присутствующих только во втором файле, и I — для строк, имеющихся в обоих файлах, но различающихся между собой. По умолчанию команда sdiff отображает все Строки обоих файлов. Приведем несколько надуманный пример, в котором сравниваются два файла, содержащие выходные данные команды who (SiMy. $ sdiff -w75 whol who2 jake uunmv jerry Jake jake vtOl ttyili ttyilj ttypl ttyp2 Sep Sep Sep Sep Sep 10 16 15 9 9 10: 11: 22: 14: 15: :37 :43 :38 :55 :19 jake jerry jake ellen carolo alison vtOl ttyilj ttypl ttyp2 ttyp5 ttyp8 Sep Sep Sep Sep Sep Sep 10 15 9 16 16 9 10: 22: 14: 12: 13: 12: :37 :38 :55 :17 :03 :49 alisin ttyp8 Sep 9 12:49 Чтобы увидеть только отличающиеся друг от друга строки, используйте опцию -s: $ sdiff 2dl uunmv 5c4,5 jake -s -w75 ttyili ttyp2 whol who2 Sep 16 11:43 Sep 9 15:19 < 1 > ellen carolo ttyp2 ttyp5 Sep 16 12:17 Sep 16 13:03 Выюдимые строки обычно имеют длину порядка 130 симюлов. Это слишком много для экрана, способного вместить лишь строку с 80 символами. Попробуйте установить 132-сим- юльный режим. Если вы не сможете этого сделать, используйте для задания более короткой строки опцию .-w (установите, например, -w80, и в строке будет отображаться 80 символов). В таком случае команда sdiff станет выводить по 37 симюлов каждой строки (но не все 80). Если вы можете установить режим сжатия при выводе на экран или имеете очень широкий экран, используйте опцию -wl70. В таком случае строки будут выводиться полностью. В параграфе 28.06 описана очень полезная возможность команды sdiff — создание в интерактивном режиме одного файла из двух сравниваемых. - JP 426 Часть четвертая. Содержимое файлов
28:07 28.05 Два файла рядом: сравнение и взаимное смещение ^^^% С помощью команды sdiff (28.04) выводимые на экран два файла можно расположить рядом, I <g) J благодаря чему их будет легче сравнивать. Имеющаяся на компакт-диске программа twin ^^4 похожа на команду sdiff, но в отличие от последней она позволяет сдвигать файлы М" относительно друг друга. Кроме того, эта программа лучше работает с длинными строками, что облегчает их чтение. Программа twin выводит файлы, разделяя экран на две части и проставляя номера строк с левого края: 1 I 2 You can use the be program to | You can also use be to 3 convert from decimal to hex. I convert from decimal to hex. 4 To do so, use obase to I To do so, use obase to 5 set the base for output: I set the base for output: 6 I 7 obase=16 |obase=16 На вашем терминале выделенная строка может быть подчеркнутой. Программа twin указывает, что эта строка в разных файлах выглядит по-разному. Для перемещения по файлам в программе twin предназначено несколько команд, которые выводятся при первом запуске программы. С помощью команды и в обоих файлах производится перемещение вверх, а с помощью команды v — перемещение вниз. Есть также команды, позволяющие перемещаться только по определенной части экрана. Так, команда j обеспечивает перемещение вниз по его правой части. - LM 28.06 Отбор вариантов с помощью команды sdiff Одна из проблем, которую вы, возможно, хотели бы решить с помощью команды dlff3 (28.02), связана с сортировкой беспорядочного текста. (Стать таковым он может после редактирования файлов несколькими людьми и последующего внесения изменений в их копии.) Часто бывает так, что какая-то информация является корректной только в одной версии файла, а какая-то — в другой. Можно ли создать одну версию данного документа, в которой будут отражены изменения, внесенные в каждую из копий? Да, конечно. Но прежде необходимо выбрать из каждой копии правильные данные. Это нетрудно сделать, задействовав команду sdiff (28.04). (Появление подобных проблем, как вы понимаете, лучше всего пресекать в корне, испоЛьзуя систему SCCS или RCS (20.12)-) Одним из самых полезных применений команды sdiff можно считать построение выходного файла путем интерактивного отбора различных версий текста из двух файлов. Для этого нужно задать опцию -о и указать имя создаваемого выходного файла. В таком случае команда sdiff после каждого набора отличий будет выводить символ %. Сравнивая две версии текста, нужно выбрать одну из них и направить ее в выходной файл. Для выбора левого столбца следует ввести 1, правого — г, для выхода из программы — q. - TOR, JP 28.07 Сравнение очень длинных файлов: команда bdiff Команда diff не способна обрабатывать очень длинные файлы. Ее опция -h обеспечивает быструю, но несколько небрежную работу, так как некоторые изменения могут быть не замечены. У пользователей System V имеется альтернатива данной команде — команда bdiff. Она игнорирует одинаковые строки в начале каждого файла, разбивает остатки файлов на куски и применяет к каждому сегменту команду diff. (Пользователи BSD для более или менее удовлетворительного выполнения этой работы могут, наверное, создать сценарий. Я не Сравнение файлов 427
28.08 пробовал.). Команда bdiff также задает номера строк для каждого сегмента, создавая впечатление, что все сделано с помощью только команды diff. Хорошо то, что команда bdiff спосоЪш найти все отличия. Однако она может найти "отличия", которые на самом деле таковыми не являются. "Обнаруживаются" они, как правило, по причине разной длины файлов. Рассмотрим в качестве примера работу команды diff с двумя не очень длинными файлами, в которых она правильно находит единственное отличие. А вот команда bdiff обнаруживает еще одно, "лишнее" отличие: % diff logl log2 11580all581 > 15:25:42: ERROR: printer offline' % bdiff logl log2 11580all581 > 15:25:42: ERROR: printer offline 15080al5080 < 17:22:59: WARNING: queue too long; waiting 15080al5081 > 17:22:59: WARNING: .queue too long; waiting Несмотря на этот недостаток команды bdiff, обойтись без нее часто бывает просто невозможно. - JP Более дружественный вывод команды diff Если вы находите вывод команды diff (Ж01) трудным для восприятия, попробуйте отфильтровать его, придав с помощью команды ediff более читабельный вид. Предположим, что программа diff вывела такой отчет: % diff chapter2 chapter2.new i22,26d21 < Use the be program to convert from decimal to hexadecimal. < To do so, use the obase command to set the base for output: < <, . . obase=16 39c34,35 < See Section 5.6 for more examples of-using be, > See section 5.6 for information on how to use the be command to > convert decimal to hexadecimal. Пока не привыкнешь к формату выюда, понять его трудно. Попробуем отфильтровать вывод с помощью команды ediff. % diff cnapter2 chapter2.new | ediff —■•■ :-- 5 lines deleted at-22r: Use the be program to convert decimal to hexadecimal. To do .so, use the obase co.mmand to set the base fpr output: obase=l6 '■—'■ 1 line changed to 2 lines at 39 from: See Section 5.6 for more examples of usirig'be. to: See section 5.6 for information on how to use the bo command to convert decimal to hexadecimal. - LM 42S Часть четвертая. Содержимое файлов 28.08 ЕЗ
28.09 28.09 Сценарии для редактора ex, создаваемые командой diff Опция -е команды diff вместо обычного вывода создает сценарий редактирования, который можно использовать с редакторами ех (м.04) или ed. Такой сценарий содержит последовательность команд а (добавить), с (заменить) и d (удалить), необходимых для воссоздания файла file2 из fllel (первого и второго файлов, указанных в командной строке diff). Очевидно, заново создавать первый файл из второго таким образом нет необходимости, так как это легко сделать с помощью команды ср. Однако редактируя сценарий, созданный командой diff, можно добиться желаемого сочетания двух версий. Рассмотрим случай, когда можно использовать эту возможность. Два сотрудника фирмы, независимо друг от друга, внесли изменения в свои копии файла, а вам нужно объединить эти версии. (Такая потребность возникает при работе в сети, когда пользователи копируют файлы из одной машины на другую. Обычно подобные проблемы порождает слабая координация работ.) Рассмотрим две версии одного и того же абзаца, которые мы хотели бы объединить: Версия 1: The Book of Kells, now one of the treasures of the Trinity College Library in Dublin, was found in the ancient monastery at Ceannanus Мог, now called Kells. It is a beautifully illustrated manuscript of the Latin Gospels, and also contains notes on local history. it was written in the eighth century. The manuscript is generally regarded as the finest example of Celtic illumination.' Версия 2: The Book of Kells was found in the ancient . monastery at Ceannanus Мог, now called Kells. It is a beautifully illustrated manuscript of the Latin Gospels, and also contains notes on local history. It is believed to have been written in the eighth century. The manuscript is generally regarded as the finest example of Celtic illumination. Как видите, каждый из файлов содержит по одной дополнительной фразе. Мы можем объединить эти абзацы в одном файле, включающем оба изменения. Если введем команду $ diff -e versionl versian2 > «xecript то получим следующий файл excscript: бс It is believed to have been written in the eighth century. 1,2c The Book of Kells was found in the ancient Возможно, вы заметили, что отличия записываются в сценарий в обратном порядке: последнее отличие идет первым. Это важно, когда изменения вносятся на основании номеров строк, так как в противном случае внесение в файл более ранних изменений нарушило бы нумерацию строк, в результате чего остальная часть сценария оказалась бы недействительной. Вы, надо полагать, заметили и то, что данный сценарий просто воссоздает файл version2, а это не то, что нам нужно. Мы xi/гим вставить в версию 1 строку 5, не заменяя ее первую и вторую строки. Поэтому сценарий необходимо отредактировать, чтобы он имел следующий вид: 6с It is believed to have been written in the eighth century. w Сравнение файлов 429
28.09 (Для того чтобы результаты работы сценария записывались в файл, мы добавили команду w.) Теперь, набрав $ ex - versionl < exscript мы получим объединенный файл: The Book of Kells, now one of the treasures of the Trinity College Library in Dublin, was found in the ancient monastery at Ceannanus Мог, now called Kells. It is a beautifully illustrated manuscript of the Latin Gospels, and also contains notes on local history. It is believed to have been written in the eighth century. The manuscript is generally regarded as the finest example of Celtic illumination. При использовании команды diff подобным образом нетрудно ошибиться, особенно если было внесено много изменений. Легко перепутать направление замен и произвести ненужные изменения. Чтобы избежать этого, вы должны придерживаться следующих рекомендаций. • В командной строке diff первым называйте тот файл, который по своему содержанию ближе к требуемому. Это позволит минимизировать размер создаваемого сценария редактирования. • Измените сценарий редактирования таким образом, чтобы он вносил только необходимые изменения, и примените его к тому же (первому) файлу. Тем не менее здесь очень легко ошибиться, поэтому будет лучше, если сценарий не станет вносить изменения непосредственно в один из ваших исходных файлов. Вы должны вместо команды w добавить в конце сценария команду %р (или 1, $р) — в таком случае результат будет направлен на стандартный вывод (u.oi). Это, как правило, является, достаточно удачным решением, если используются сложные сценарии редактирования. Если такая команда применяется в сценарии, то командная строка для действительного внесения изменений в файл будет выглядеть так: $ ex - versionl < exscript > versions У писателей, вносящих в процессе работы над произведениями много изменений, часто возникает потребность вернуться назад, с тем чтобы частично восстановить первоначальную версию. В этом случае, очевидно, может выручить частое резервное копирование. Однако если места для хранения резервных копий недостаточно, то сохранять можно только одну из ранних версий файла, а затем с помощью команды diff -e дополнять сценарий редактирования, отражая в нем различия, появляющиеся в каждой из следующих версий. (Это как раз то, что делают системы управления версиями SCCS и RCS (20.12).) Чтобы применить несколько сценариев к одному файлу, можно просто передать их по каналу редактору ех, не переадресовывая ввода: cat 25.02 $ cat scriptl script2 scripts I ex - oldfile Минуточку! А как же быть при необходимости передать в канал команду w (или %р)? Вы можете отредактировать последний сценарий и ввести в него одну из этих команд. Но есть и другой прием, который мы просто обязаны рассмотреть, так как он иллюстрирует еще одну полезную возможность интерпретатора shell, о которой многие не знают. Если список команд, разделенных точкой с запятой, заключить в скобки (Ч.от), то стандартные выводы всех этих команд будут объединены и их можно будет переадресовать всех вместе. Так, если вы наберете cchoj.ee $ cat scriptl script2 scripts; echo '%p' | ex - oldfile то результат выполнения команды cat будет направлен, как обычно, на стандартный вывод и только результат выполнения команды echo попадет через канал команде ex. Но если вы наберете $ (cat scriptl script2 script3; echo '%p') | ex - oldfile то вывод всей цепочки команд попадет в канал, чего и требовалось добиться. — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 430 Часть четвертая. Содержимое фатов
28.12 28.10 Проблемы, связанные с командой diff и позициями табуляции Команда diff ps.oi) вставляет в начале строки дополнительные символы (>, <, + и т.д.). Это может вызвать неприятности, связанные с изменением позиций табуляции, так как дополнительные символы способны настолько сдвинуть строки, что отступы станут нерациональными. Команда diff-t меняет каждый символ табуляции на 8 пробелов и решает проблему. Однако при использовании нестандартных позиций табуляции передача вывода команды diff по каналу посредством команды expand или рг -е (см. параграф 41.04): % diff afile bfile | expand -4 не дает требуемого результата, поскольку команда diff уже вставила дополнительные символы. Лучшим решением, с которым мне когда-либо приходилось встречаться, является использование оператора подстановки процесса < () в интерпретаторе bash и сценария с именем / (восклипательный знак) (9.18). Символы табуляции можно заменить до того, как они попадут в команду diff. Например, чтобы показать различия между двумя файлами, содержащими 4-символьные позиции табуляции, используйте следующие команды: [9.18] /\> bash$ diff < (expand -4 afile) <(expand -4 bfile) bash % diff '! expand -4 afile' '! expand -4 bfile' другие интерпретаторы shell JP 28.11 Программы cmp и diff Программа cmp, также предназначенная для сравнения файлов, несколько проще команды diff (2&oi). (Ф ] (На компакт-диске имеется ее GNU-версия.) Эта программа сообщает, являются ли файлы одинаковыми, а также определяет смещение (в байтах), начиная с которого стали встречаться СЙФ отличия. Детального анализа обнаруженных различий вы не получите. Часто, особенно если сравниваются ASCII-файлы (5Ш), программа cmp работает быстрее, так как не нужно генерировать длинный отчет, содержащий информацию о произведенных изменениях. Если вам просто необходимо узнать, различаются файлы или нет, данный инструмент вполне подойдет. Следует, однако, заметить, что программа cmp не всегда работает быстрее. Отдельные версии команды ^способны выполнять такие простые операции, как сравнение размеров файлов. Если два двоичных файла имеют неодинаковые размеры, значит, они разные. Некоторые реализации команды diff сообщат вам об этом, даже не производя никакой дополнительной обработки. Обе команды — и diff, и cmp — возвращают код завершения (44.от), свидетельствующий о том, что именно эти команды обнаружили. Код завершения Значение Файлы одинаковы Файлы отличаются Ошибка Для сценариев код завершения часто более важен, чем сам вывод команд. - ML 28.12 Сравнение двух файлов с помощью команды comm Команда comm определяет, какая информация является общей для двух списков, а какая относится только к одному из них. Предположим, что вы собираете информацию о любимых фильмах критиков Сискеля (Siskel) и Эберта (Ebert). Фильмы перечислены в отдельных файлах (и должны быть отсортированы (Зб.оо; если это не так, воспользуйтесь сценарием с именем / (я.щ). Для упрощения примера допускаем, что оба списка являются короткими: % cat siskel Citizen Kane Сравнение файлов 431
Шг Halloween VI. Ninja III Rarabo II Star Trek V Zelig % cat ebert Cat People Citizen Kane My Life as a Dog Q Z Zelig Чтобы сравнить любимые фильмы двух критиков, введите % сопла siskel ebert Cat People Citizen Kane Halloween VI My Life as a Dog Ninja III Q Rambo II Star Trek V Z Zelig В первом столбце приведены названия фильмов, которые нравятся только Сискелю, во втором — только Эберту, а в третьем — им обоим. Вы можете подавить вывод одного или нескольких столбцов, указав его номер в опции командной строки. Например, чтобы подавить вывод первого и второго столбцов (выводить только названия фильмов, которые нравятся обоим критикам), нужно ввести % coram -12 aiskel ebert Citizen Kane Zelig Другой пример. Предположим, что вы получили последнюю версию программного обеспечения (Release 4) и ваша задача — выяснить, какие библиотечные функции являются новыми, с тем чтобы добавить их к уже имеющимся. Причем списки функций Release 3 (r3Jisf) и Release 4 (r4_list) у вас уже есть. (Если списков нет, то их можно создать, для чего придется перейти в каталог, содержащий страницы руководства по этим функциям, вывести список файлов с помощью команды Is и сохранить его в файле.) В приведенных ниже списках для представления функций использованы буквы алфавита: % cat r3_list b с d f g h % cat r4_list a b ' с d e f 432 Часть четвертая. Содержимое файлов
28.13 Теперь, воспользовавшись командой сотт, можно ответить на несколько интересующих нас вопросов. • Какие функции являются новыми в Release 4? Ответ: % сошп -13 r3_list r4_list Вывод 2-го столбца (только в Release 4) а е • Какие функции исключены из Release 4? Ответ: % comm -23 r3_list r4_list вывод 1-го столбца (только в Release 3) Я h • Какие функции сохранены в Release 4? Ответ: % comm -12 r3_list r4_list Вывод 3-го столбца (обшие функции) Ъ с d f Мы можем создать отдельные списки, сохранив вывод предыдущих команд в трех файлах. Команда comm выполняет сравнение только сортированных файлов. Если вы по какой-то причине не можете отсортировать их, попытайтесь воспользоваться командами dtff и grep (см. параграф 2.14). - DG 28.13 Программа make — не только для программистов Программа make — это средство UNIX, предназначенное для описания зависимостей между группой связанных файлов, обычно относящихся к одному проекту. Широкую популярность оно завоевало в первую очередь среди разработчиков программных проектов. Программа make описывает пути создания других программ: какие исходные файлы нужно скомпилировать, какие библиотеки включить и какие объектные файлы компоновать. Благодаря отображению всех этих зависимостей в одном файле отдельные члены коллектива разработчиков программного обеспечения могут внести изменения в один модуль, запустить. программу make и быть уверенными, что она отобразит последние изменения, внесенные другими членами коллектива. Мы поставили программу make в один ряд с другими командами нахождения различий между файлами по своему усмотрению. Она, конечно, не позволяет сравнивать, отдельные версии исходных файлов, но ее можно использовать для сравнения исходного и форматированного файлов. UNIX считается эффективной средой для обработки текстов частично благодаря тому, что в ней можно найти новые применения для стандартных программ. Утилита make, например, широко используется при разработке проектов по созданию документации. Одним из ее применений является поддержание актуальности форматированных файлов, входящих в единый документ, и предоставление пользователям способа получения целого документа без необходимости. знать, какой из препроцессоров либо какую из опций программ nroff или troff (43.13) необходимо вызвать. Одна из основных операций, которую осуществляет программа make, — это сравнение двух наборов файлов, например форматированных и неформатированных, и определение того, являются ли члены одного набора (неформатированные файлы) более новыми по сравнению с их аналогами в другом наборе (форматированные файлы). Осуществляется это путем простого сравнения времени последней модификации (i6.es) (временных характеристик) пары файлов. Если неформатированный исходный файл изменился со времени последнего форматирования одноименного файла, то программа make выполнит заданную команду и "пересоздаст" форматированный файл. I""1 Сравнение файлов 433
28.13 Чтобы получить возможность использовать программу make, необходимо создать файл описания, который обычно называется makefile или Makefile и находится в рабочем каталоге проекта. В этом файле, как правило, описывается иерархия зависимостей между отдельными файлами, называемыми компонентами. На вершине иерархии указывается действие, определяющее цель проекта. В нашем случае мы можем представить цель как создание печатной копии книги. Компонентами являются форматированные файлы, генерируемые при обработке неформатированных файлов с помощью программы nroff. Как выглядит файл makefile, отображающий такие зависимости, показано ниже. Эта же иерархия представлена и на рис. 28.1. manual: chOl.fmt ch02.fmt ch03.fmt Ip 43.02 !p ch0[l-3).fmt chOl.fmt: chOl nroff -mm chOl > chOl.fmt ch02.fmt: ch02 lb! 43.15 tbl ch02 I nroff -mm > ch02. fmt ch03.fmt: спОЗа спОЗЬ спОЗс nroff -mm ch03[abc] > ch03.fmt Рие. 28.1. Иерархия файлов и команд, необходимых для создания документа Manual Нашей целью является создание документа manual, состоящего из трех форматированных файлов, имена которых перечислены после двоеточия. Каждый из этих компонентов имеет собственную строку зависимостей. Например, файл chOl.fmt связан с исходным файлом chOl. Под строкой зависимостей находится команда, генерирующая файл chOl.fmt Каждая командная строка должна начинаться с символа табуляции. После ввода команды make три форматированных файла будут направлены на принтер. Однако этому конечному действию предшествует целая последовательность операции. Анализируются строки зависимостей каждого компонента, что позволяет определить, был ли модифицирован исходный файл со времени создания форматированного файла. Команда форматирования будет выполнена только в том случае, если исходный файл является более поздним. После создания всех компонентов выполняется команда fe (43.02). Иллюстрируя этот процесс, предположим, что ни один из трех форматированных файлов не изменился. При редактировании исходного файла ch03a изменяется его время модификации. 434 Часть четвертая. Содержимое файлов
28.14 В результате выполнения команды make будет переформатирован любой выходной файл, связанный с сИОЗа: $ make nroEE -mm ch03[abc] > ch03.fmt lp ch0[l-3] .fmt Переделывать необходимо лишь файл ch03.fmt. Как только команда форматирования будет завершена, начнет выполняться командная строка, расположенная под строкой описания manual и передающая файлы на принтер. Хотя в данном примере задействованы не все возможности программы make, мы надеемся, что он подскажет вам другие способы применения этой программы при разработке проектов. Вы можете пользоваться таким же простым файлом makefile, как этот, или же изучить дополнительные средства, например встроенные макросы, и попытаться обобщить файл описаний для универсального его применения. — TOR, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 28.14 Другие способы использования программы make Чем чаще я задумываюсь о программе make, тем больше применений для нее нахожу. Чтобы лучше познакомиться с возможностями команды, посмотрим, как с ее помощью можно следить за другими файлами makefile. Одним из моих любимых является файл makefile, предназначенный для обновления базы данных NIS (раньше она называлась ур, или Yellow Pages — желтые страницы). Мне нравится этот файл потому, что он позволяет выполнить такую сложную задачу, как обновление распределенной базы данных. (Кстати, эта задача прекрасно "подходит" для программы make.) Этот файл makefile довольно сложен, поэтому я не буду анализировать его построчно, а лишь представлю схему работы. Системный администратор обновляет один или несколько файлов (например, файл passwd) и хочет внести свои изменения в базу данных ур. Поэтому необходимо проверить, является ли файл паролей более новым, чем база данных. К сожалению, последняя не представлена одним файлом. Файл makefile, контролирующий базу данных NIS, обходит эту проблему путем создания пустых файлов, служащих в качестве временных меток. Для каждой базы данных NIS создается отдельный файл с расширением .time. При вводе команды make проверяются все файлы базы данных и файлы с временной меткой. Программа make знает, что если главный файл новее, чем файл .time, значит, нужно перестроить часть базы данных. После такой перестройки файл makefile изменяет (с помощью команды touch) временного метку, с тем чтобы она отображала время, когда база данных была перестроена. Файл makefile имеет примерно такой вид: passwd: passwd.time passwd.time: /etc/master/passwd @ множество команд для перестройки базы данных touch 21.07 @ touch passwd. time @ другие команды обработки распределенной базы данных hosts: hosts.time hosts.time: подобные команды Вам, возможно, никогда не понадобится создавать такой сложный файл makefile, но нужно искать ситуации, в которых программу make можно использовать по-новому. Она предназначена не только для программистов. - ML Сравнение файлов 435
28.15 28.15 Отображение изменений в troff-файле с помощью команды diffmk При создании многочисленных черновиков какого-либо документа в более новых версиях удобно проставлять метки изменений, обозначающие места, в которых были сделаны дополнения или произведены удаления. Макрос .mc (margin character — символ границы) программы troff (43./3) можно использовать для печати меток изменения на поле документа, форматируемого данной программой. Внести этот макрос в документ можно с помощью команды diffmk, синтаксис которой приведен ниже: % diffmk version.1 version.2 marked_file Данная командная строка сравнивает старую версию файла (venion.J) с новой (version.2) и создает третий файл — markedjile. Этот файл состоит из содержимого файла version.2, a также макросов .тс, указывающих, в каких местах этот файл отличается от version. 1. Когда файл markedjile форматируется, то добавленный и измененный текст помечается на полях вертикальной чертой (|), а подлежащий удалению — звездочкой (*). Иногда возникает необходимость запустить команду diffmk для нескольких файлов одновременно. Предположим, например, что в нашем каталоге project содержатся восемь глав первой версии документа: % Is -F project chapters.old/ stuff % Is project/chapters.old chOl ch02 ch03 ch04 ch05 ch06 ch07 ch08 Перед созданием второй черновой версии мы скопируем файлы глав в каталог chapters, new. % le -T project chapters.new/ chapters.old/ stuff % cd project/chapters.new % Is chOl ch02 ch03 ch04 ch05 ch06 ch07 ch08 Копирование файлов позволяет редактировать новые версии, сохраняя исходные в старом каталоге. После редактирования файлов в новом каталоге запускаем для них команду diffmk, набрав % diffmk ../chapters.old/chOl chOl chOl.diffmk % diffmk ../chapters.old/ch02 ch02 ch02.diffmk % diffmk ../chapters.old/ch03 ch03 ch03.diffmk Выполнять данные команды можно в цикле (9.и), что позволит уменьшить объем необходимого ввода. Кроме того, можно сэкономить дисковое пространство, используя известные нам системы управления версиями SCCS и RCS (2o.ii). - DG 436 Часть четвертая. Содержимое файлов
29 Проверка правописания, подсчет слов и анализ текста 29.01 Команда spell Команда spell читает один или несколько файлов и выводит список слов, содержащих ошибки. Вы, конечно, можете переадресовать вывод в файл, применить команду grep (2zot) для нахождения каждого слова, а затем использовать редактор w или ех для внесения исправлений. Можно также написать сценарий, который в интерактивном режиме показывал бы ошибочно написанные слова и исправлял их по команде. Но реализовать все это на практике для большинства пользователей слишком сложно. (Почти все названные проблемы способна решить программа ispell (29.02).) При обработке файла с помощью команды spell создаваемый список обычно содержит много слов, написанных правильно, но не распознанных программой. Команда spell чувствительна К регистру: она воспринимает, к примеру, слово Aaron, но "жалуется" на слово аагоп. Поэтому вам придется убирать имена собственные и другие слова, которых команда spell не знает, из списка настоящих ошибок. Посмотрим на результаты обработки предложения, взятого в качестве примера: Alcuin uses Transcript to convert ditroff into PostScript output for the LaserWriter printerr. $ spell sample Alcuin ditroff printerr LaserWriter PostScript Transcript Как видите, только одно слово из этого списка на самом деле написано с ошибкой. Во многих UNIX-системах можно создавать персональные файлы словаря, что позволит команде spell распознавать специальные слова и термины, характерные для конкретных приложений. Для этого после выполнения команды spell и просмотра предоставленного ею списка слов нужно создать файл, содержащий слова, которые в действительности являются корректными. Команда spell будет проверять этот список после просмотра собственного словаря. [В тех системах, в которых мне приходилось работать, список слов всегда был упорядоченным (зьм). — JP\ Если специальные термины помещены в файл с именем diet, то этот файл можно указать в командной строке с помощью опции +: $ spell +dict eample printerr Теперь приведенный выше вывод сократился до одного неверно написанного слова. Проверке правописения, подсчет слов и анализ текста 437
29.02 Команда spell также пропускает слова, заданные в качестве аргументов для макросов программы nroffwm troff (43.13), и, подобно любой другой программе проверки правописания, допускает ошибки, связанные с неправильным образованием слов от корней, содержащихся в ее словаре. Понимая, как работает команда spell (29.04), вы будете меньше удивляться подобным ошибкам. — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 29.02 Интерактивная проверка правописания с помощью программы ispell Первая UNIX-программа проверки правописания spell (29.00 хороша как выполняющая быструю проверку небольших по объему документов, но всем хотелось бы иметь более удобную программу, которая не только показывала бы неправильно написанные слова в контексте, но и предлагала бы варианты их замены. |^^| Очень удобная программа ispell, которая была перенесена в UNIX и с годами улучшена, Г С*1 | делает все это, а также многое другое. Расскажем о некоторых способах ее использования. ^^л4 Как и в случае программы spell, программе ispell необходимо указать имя обрабатываемого ispell документа. Но на этом сходство двух программ заканчивается. Программа ispell занимает весь экран и выводит две строки контекста в нижней его части. Если ваш терминал поддерживает режим негативного отображения, то "подозрительные" слова будут выделены. Несколько возможных вариантов для замены каждого такого слова представляются в левом верхнем углу экрана — это слова из словаря программы ispell, которые отличаются от выделенного только одной буквой, т.е. содержат пропущенную, дополнительную или переставленные буквы. Если вам встретилось слово, выделенное программой ispell как ошибочное, воспользуйтесь одной из восьми перечисленных ниже команд. [ПРОБЕЛ] Нажмите клавишу [ПРОБЕЛ], если написание выделенного слова менять не следует. А Введите команду А, если написание слова необходимо оставить неизменным во всем файле. I Задайте команду I, если написание слова необходимо оставить здесь и в остальной части файла, а также если это слово нужно ввести в персональный словарь. По умолчанию текущий словарь содержится в файле .ispelljwords в начальном каталоге, но место его нахождения можно изменить с помощью опции -р или путем занесения в переменную среды (б.оо WORDLIST имени файла словаря. Если вы работаете с текстами, скажем, по компьютерной тематике, то такая возможность чрезвычайно удобна, поскольку вам приходится оперировать большим количеством жаргонных и профессиональных терминов. "Научите" этим словам программу ispell, и она больше не будет пытаться их исправить. (Одна особенность: при задании альтернативного файла используйте полное путевое имя ().)Я), иначе программа ispell будет искать его в вашем начальном каталоге.) 0-9 Введите цифру, соответствующую одному из предлагаемых программой ispell вариантов правильного написания слова. Например, если вы ввели hnadle, как это сделал я при наборе данного параграфа, то программа ispell предложит вариант замены в виде 0: handle в левом верхнем углу экрана. При вводе 0 производится замена и переход к следующей ошибке, если она имеется. R Задайте команду R, если ни один из предлагаемых программой вариантов вам не подходит, а затем введите нужное слово, и операция замены будет выполнена. L Задайте команду L, если программа ispell не предложила ничего подходящего, а вы не знаете, как правильно пишется данное слово. Программа ispell предложит вам ввести строку поиска. В качестве метасимвола можно ввести * (соответствует не более чем одному символу). После этого будет выведен список подходящих слов из словаря программы. 438 Часть четвертая. Содержимое файлов
29.03 Q Введите команду Q, и вы выйдете из программы с сохранением всех произведенных изменений; оставшиеся ошибки будут проигнорированы. X Введите команду X при необходимости выйти из программы без внесения каких-либо изменений. Но это не все! Программа ispell сохранит копию вашего исходного файла с расширением Ьак на тот случай, если вы откажетесь от внесенных изменений. Если вы не хотите, чтобы программа создавала Aafc-файлы, вызывайте ее с опцией -х И еще об одной уникальной особенности программы ispell: она "знает" о существовании прописных букв. Программа уже "выучила" имена собственные и множество общепринятых сокращений, может работать со словами типа ТЕХ, с необычными прописными буквами. Что касается программы ТЕХ (43.12), то ispell даже имеет специальный режим, в котором она распознает конструкции языка ТЕХ. (К сожалению, программа ispell ничего не знает о программе troff (43.13).) О других возможностях этой прекрасной программы можно узнать со страниц руководства, хранящихся на диске вместе с программой ispell. - TOR 29.03 Проверка правописания слова Если вы не знаете, какой из двух предложенных вам вариантов написания слова правильный, воспользуйтесь командой spell без применения аргументов. Введите имя команды, а затем нажмите клавишу [RETURN]. Далее введите имеющиеся у вас варианты написания слова и нажмите, прямо в строке, клавиши [CTRL-d], чтобы завершить список. Команда spell выведет список слов, которые она относит к неправильным: $ spell misspelling mi spelling ICTRL^dl mispelling Таким способом можно вызвать команду spell из редактора vi, если ввести :! 30.26 : ! spell misspelling mispelling ICTRL-^dl mispelling [Hit return to continue] При использовании программы ispell добавьте опцию -/, поскольку эта программа по умолчанию не читает стандартный ввод. (Даже опция -/ не позволяет программе ispell читать данные из канала. Указанная опция предназначена для ввода списка проверяемых слов, таким же способом, как это делалось в программе spell. Когда вы завершите формирование списка, программа ispell выведет слово с ошибкой. Она не имеет каких-то дополнительных функциональных возможностей, если не считать того факта, что она использует персональные словари и более точные алгоритмы проверки правописания.) Перечисленные действия в некоторых случаях целесообразнее выполнить с помощью программы look (27. щ. Укажите только один аргумент, и программа look начнет искать в системном файле /usr/dict/words слова, начинающиеся с заданных символов. Это хороший способ проверки правописания или поиска связанных слов: % look help help helpful helpmate Проверка правописания, поцсчет слов и анализ текста 439
29;04 По умолчанию программа look при просмотре списка слов используется с опциями -df. Опция -d обеспечивает игнорирование небуквенных символов, чисел, пробелов и символов табуляции. При наличии опции -/строчные и прописные буквы не различаются. -DD.JP 29.04 Работа команды spell [Имея программу ispell (29.02), вы свободно можете обойтись без команды spell: Программа ispell не только мощнее, в ней проще обновлять словари. Тем не менее мы решили представить вашему вниманию информацию, которая пролила бы свет на правила, используемые программами проверки правописания при сверке слов с имеющимися в словаре. — TOR] Во многих UNIX-системах каталог /usr/lib/spell содержит главную программу, вызываемую командой spell, вместе с дополнительными программами и файлами данных. % 1в -1 /usr/lib/spell total 888 -rwxt-xr-x -rwxr-xr-x -rwxr-xr-x -rw-r—r— -rw-r—r— -rw-r—r— -rw-rw-rw- -rwxr-xr-x -rwxr-xr-x 1 1 1 1 1 1 1 1 1 bin btn bj.n bin bin bin root bin bin 545 16324 14828 53872 53840 6336 252312 21634 23428 Dec Dec Dec Dec Dec Dec Nov Dec Dec 9 9 9 9 9 9 27 9 9 1988 1988 1988 1988 1988 1988 16:24 1988 1988 compress hashcheck hashmake hlista hlistb hstop spellhist spellin spellprog В некоторых системах команда spell является сценарием, который обрабатывает входные данные с помощью команд deroff -w (2».ю) и sort -и (Зб.ов), удаляя форматирующие коды и формируя упорядоченный список слов (по одному слову в строке). В других системах это самостоятельная программа, сама выполняющая эти действия. Поддерживаются два отдельных списка слов: один составлен по правилам американского правописанйя.'другой — по правилам британского правописания (вызывается опцией -Ь команды spell). Данные списки (hlista и hlistb) не могут быть прочитаны и изменены напрямую. Это сжатые файлы, полученные из списка слов, представленных в виде девятиразрядных, хэш-кодов. (Хэш-кодирование — специальная методика, используемая для быстрого поиска информации.) Главной программой, вызываемой командой spell, является spellprog. Она загружает в специальную таблицу список хэш-кодов (либо из файла hlista, либо из файла hlistb) и ищет хэш-код, соответствующий каждому слову в упорядоченном, списке слов. Причем слова (или хэш-коды), имеющиеся в словаре, не рассматриваются. Для остальных слов программа spellprog путем выполнения различных операций над основой слова, базирующихся на правилах использования суффиксов и префиксов, пытается образовать правильные производные слова. При этом выполняются некоторые из следующих операций: -y+iness +ness *-y+i+less +less-y+ies -t+ce -t+cy Новые слова, созданные с помощью этих операций, повторно сверяются со словарем. Однако прежде чем будут применены правила словообразования, эти слова нужно сверить с таблицей хэш-кодов, построенной из файла hstop. Этот список содержит типичные ошибки, которые операции словообразования могут пропустить. Например, ошибочное слово thier может быть преобразовано в thy вследствие применения правила добавления суффиксов -y+ier. В файле hstop учтено максимально возможное количество ошибок такого типа. Конечная часть вывода состоит из слов, не обнаруженных в словаре даже после того, как программа пыталась найти их по основе, а также слов, содержащихся в списке типичных ошибок. 440 Часть четвертая. Содержимое фатов
Сказанное проще усвоить на практике, используя команду spell с опцией -v или -х. Опция -v исключает последний поиск в таблице и обеспечивает вывод слов, которых нет в словаре, вместе с производными от них словами. Она позволяет увидеть, какие слова были найдены в результате выполнения операций словообразования; при этом показывается, какие правила были использованы (см. параграф 29.01). % spell -v sample Alcuin ditro.ff LaserWriter PostScript printefr Transcript +out output +s uses Опция -x заставляет команду spell начинать работу с этапа словообразования и выводить информацию обо всех предпринимаемых попытках найти основу каждого слова. % spall -х sample =into LaserWriter =LaserWrite =LaserWrit LaserWriter =laserWrite =laserWrit =output =put LaserWriter Перед основой ставится знак равенства (=). В конце вывода идет перечень слов, основа которых в словаре не найдена Вам необходимо знать назначение еще одного файла — spellhist. В некоторых системах при каждом запуске команды spell ее вывод добавляется посредством команды /ее аз.оу в этот файл, формируя, по сути, для вашего компьютера список всех ошибочных и неопознанных слов. Файл spellhist является своего рода мусорной корзиной, объем которой постоянно увеличивается. Поэтому его содержимое периодически следует удалять. Чтобы получить из этого файла полезную информацию, например список неверно написанных слов или специальных терминов, встречающихся чаще всего (см. пример в параграфе 29.07), нужно воспользоваться командами sort и uniq ^c (is.20). Новые слова можно добавить в словарь, но этот процесс слишком сложен, поэтому мы не станем его здесь описывать. Наверное, проще обратиться к персональному словарю (2Ш). А еще лучше использовать более мощную программу ispell, в которой обновлять используемые словари (29.es) намного проще. — DD, из книги UNIX Text Processing издательства Hayden Books, 1987 г. 29.05 Добавление слов в словарь программы ispell Программа ispell (29.02) для проверки правописания использует два списка: главный и дополнителнный персональный. Главный список слов для программы ispell обычно содержится в файле /usr/local/lib/ispell/is- pelljiash и представляет собой "хэшированный" файл словаря. С целью ускорения процесса проверки правописания он сжимается посредством программы buildhash, которая поставляется вместе с программой ispell. Проверка правописания, подсчет спов и анализ текста 441
9.05 Персональный список слов обычно содержится в файле .ispell_words, хранящемся в начальном каталоге. (Эту стандартную установку можно отменить с помощью опции -р в командной строке либо воспользовавшись переменной среды (6.oi) WORDLIST.) Файл .ispell_words содержит только список слов (по одному на строку), поэтому его легко редактировать, добавляя, изменяя или удаляя записи. Персональный список слов обычно используется в качестве дополнения к главному, поэтому если применение определенного слова будет разрешено хотя бы одним из этих списков, то оно не будет выведено программой ispell в качестве ошибочного. Персональные словари предназначены в первую очередь для проверки документов, содержащих жаргонные слова или специальные технические термины, которых нет в главном словаре, но могут использоваться для личных целей пользователей, например для хранения имен корреспондентов. Можно иметь несколько пользовательских словарей, отвечающих специальным требованиям. Дополнять персональный список слов целесообразно при каждом сеансе работы с программой ispell. Задавая команду I, вы сообщаете программе, что слово, которое она считает ошибочным, на самом деле является правильным и что его следует добавить в словарь. Можно также пополнять словарь словами из указанного файла, используя опцию -а. В каждой строке должно находиться одно слово, причем сортировка не обязательна. Каждое слово, которое нужно добавить, должно набираться со звездочкой впереди. (Этого требует особенность команды ispell -а.) В частности, вы могли бы добавить в персональный словарь сразу весь список имен утилит UNIX, когда они появляются в процессе проверки правописания. Однако когда с одними и теми же техническими терминами работают многие сотрудники, было бы нецелесообразно каждому из них добавлять одни и те же слова в свой персональный файл .ispell_words. Проще договориться иметь общий для группы словарь специальных терминов и всегда так устанавливать переменную WORDLIST, чтобы она указывала на соответствующий общий каталог. Если персональный список слов становится слишком длинным, можно создать сжатый список слов. Сценарий munchlist, который поставляется с программой ispell, сокращает слова в списке и превращает его в список корней и разрешенных суффиксов. Создается он на основании правил, описанных на странице ispell{$) документации, которая инсталлируется вместе с программой ispell с компакт-диска. В результате вы получите более компактный редактируемый файл. Можно также с помощью опции -d создать список слов, альтернативный главному. При этом, однако, возникают две проблемы. 1. Главный список должен содержать слова, являющиеся всегда правильными, независимо от контекста. Его не следует перегружать терминами, которые в отдельных контекстах могут оказаться ошибочными. Например, слово Perl, являющееся названием мощного языка программирования, в определенном контексте может быть воспринято как слово pearl, написанное с ошибкой. При документировании утилит UNIX слово perl, наверное, лучше поместить не в главный, а в дополнительный список. 2. При использовании опции -d необходимо указывать хэшированный файл словаря. Это большой файл, на создание которого уходит много времени. Редактировать его невозможно, так как для этого следовало бы отредактировать текстовый вариант списка слов, а затем с помощью сценария buildhash построить новый хэшированный словарь, обеспечивающий большую скорость проверки правописания. Для построения нового хэшированного списка слов сценарию buildhash предоставляется полный список слов, которые необходимо включить, — по одному на строку. Сценарий buildhash может обрабатывать только исходный, не сжатый сценарием munchlist список слов. В качестве такового для многих систем вполне подходит стандартный системный список слов /usr/dic/words. Содержащий его файл доступен для записи только системному администратору и, наверное, не должен при каждом случае изменяться. Поэтому вы при редактировании должны создать копию такого файла. После его обработки с помощью утилиты buildhash вы можете либо заменить этим файлом стандартный файл .ispell.hash, либо, воспользовавшись опцией -d, указать этот файл в качестве нового хэш-файла. - TOR, LK 442 Чвсть четвертая. Содержимое файлов
29.06 29.06 Подсчет строк, слов и символов Команда wc подсчитывает количество строк, слов и символов в указанном файле. (Как и большинство утилит UNIX а.зо), утилита wc, если не указать имя файла, читает данные со стандартного ввода.) Приведем в качестве примера результаты обработки с помощью указанной команды файла letter. Как видите, он содержит 120 строк, 734 слова и 4297 символов: % wc letter 120 734 4297 letter Вы можете подсчитать количество отдельных элементов, задав опции -/ (подсчет только .строк), -w (подсчет только слов) и -с (подсчет только символов). Можно, например, подсчитать количество строк в файле: % wc -1 letter 120 letter или количество файлов в каталоге: % cd тапрадез- % Is | wc -w 233 В первом примере команда wc обрабатывает файл, во втором вывод команды Is передается по каналу на ввод команды wc. (Убедитесь, что опция -а (i6.ii) заставляет команду Is выводить "скрытые" файлы. Если команда Is является псевдонимом команды Is с опцией -а, добавляющей слова в обычный вывод (например, опция -/ команды Is добавляет строку total ллл), то вы, возможно, не получите нужного результата.) Тот факт, что вывод другой команды можно пропустить через команду wc, позволяет использовать ее для выполнения операций сложения и вычитания. Однажды я написал сценарий, который, кроме всего прочего, разделял файлы на несколько частей. Мне нужно было, чтобы он следил за количеством создаваемых файлов. (Этот сценарий применяет к каждому файлу команду csplit ps.io), создавая произвольное количество файлов с именами /lie.00, /lie.01, file.02 и т.д.) Для решения этой задачи я- использовал такой код: before='ls $file* | we -Г # подсчет файлов разделение файла с помощью команды csplit after='ls $file* | wc -Г # подсчет файлов вместе с новыми num_files='expr $after - $before' # вычисление разницы Следующая команда, которую можно рассматривать как еще один трюк, сообщает, насколько больше слов содержится в файле new.file, чем в файле old.file: % ехрг "wc -w < new.file' - 'wc -w < old.file' [Команда ехрг работает во всех интерпретаторах shell, хотя в С shell и Korn shell есть встроенные арифметические команды, не требующие использования данной команды. — JP] Чтобы команда wc читала входные файлы, необходимо использовать оператор <. Если же ввести команду % ехрг 'wc -w new.file' - "wc -w old.file' то в выражении будут фигурировать имена файлов и это вызовет синтаксическую ошибку.* Используя тот же подход, приведем простой сценарий для определения разницы в количестве слов в двух файлах: Я^*"^| count_l="wc -w < $1" # количество слов в файле filel Г ® I count_2='wc -w < $2" # количество слов в файле file2 count.it diff_12='expr $count_l - $count_2' # разность двух значений * Можно было бы также ввести cat new.file I wc -w, но для этого необходимо задействовать две команды, что менее эффективно азмг). '...' 9.16 ехрг 49.06 Проверка правописания, подсчет слов и аиапиз текста 443
29.06 # Если значение $diff_12 отрицательно, поменять порядок вывода значений # и не показывать минус: case "$diff_12" in -*) echo "$2 has 'expr $diff_12 : '-\(.*\)'" more words than $1" ;; *) echo "$2 has $diff_12 more words than $1" ;; esac Если этому сценарию присвоить имя count.it, то вызывать его можно будет примерно так: % count.it draft.2 draft.1 draft.1 has 23 more words than draft.2 После соответствующих изменений сценарий сможет подсчитывать количество строк или символов. Примечание: Если значения, полученные при подсчете количества строк или символов, будут небольшими, то в выводе команды wc появятся ведущие пробелы. Это может стать причиной возникновения проблем в сценариях. Например, в приведенном выше сценарии команда echo "$1 has $count_l words" могла бы вывести такой результат: draft.2 has 79 words Видите дополнительные пробелы? Вам необходимо понять механизм действия кавычек (S.U) в интерпретаторах shell. Предоставьте интерпретатору возможность прочесть вывод команды wc и. удалить лишние пробелы. Так, в отсутствие кавычек интерпретатор shell передает четыре отдельных слова команде echo, а она вставляет между ними один пробел; echo $1 has $count_l words и мы получаем такой результат: draft.2 has 79 words Этот механизм особенно важно понимать при использовании команды wc с командами наподобие test или ёхрг, которые не допускают применения пробелов в своих аргументах. Если вы не можете использовать интерпретатор shell для удаления лишних пробелов, то удалите их, обработав вывод команды wc командой if -d ' ' (зз.и). : И наконец, несколько замечаний относительно размера файла. • Команда wc -с не является эффективным средством подсчета символов в большом количестве файлов — слишком много времени уходит на открытие каждого файла. В четвертом или пятом столбце вывода команды Is -l (это зависит от ее версии) указывается количество символов в файле, причем файл не открывается. Значения, выведенные командой Is -I для нескольких файлов, можно сложить с помощью команды addup (49.07): % Is -1 файлы | addup 4 670518 • Подсчет количества символов (как в приведенном выше примере) не позволяет точно определить полный объем дискового пространства, занимаемого файлом". Объясняется это, в частности, тем, что каждый файл занимает не менее одного дискового блока. Точные сведения об этом позволяет получить только команда du (24.09). - JP 444 Часть четвертая. Содержимое файлов
29.08 29.07 Подсчет количества повторений слов Ш*"^Ш Сценарий wordfreq подсчитывает, сколько раз каждое слово встречается в файле. Если ему | (ф) 1 предоставить несколько файлов, то данные будут читаться из них, иначе — из стандартного ^„-4 ввода. Опция -/" обеспечивает преобразование прописных букв в строчные (слова, написанные wordfreq прописными буквами, будут подсчитываться так же, как и слова, состоящие из строчных букв). Вот какой результат мы получили, пропустив через команду wordfreq предисловие этой книги: % wordfreq chOO 141 the 96 to '64 and 84 of 7t-a 55 in 44 that 38 book 32 we Следующий сценарий взят из старой заметки Карла Брандауэра (Carl Brandauer), помещенной в Usenet (из). Приведем этот сценарий, внеся в него лишь незначительные изменения: cat $* | # Команда tr читает, стандартный ввод, trJ5./7 tr "[A-Z]" "[a-z]" | # преобразует прописные буквы в строчные, tr -cs "a-z1" "\012" | # заменяет все символы, не лежащие в диапазоне a-z, # кроме символа ', символами новой # строки, т.е. располагает по слову в строке. son36.01 sort- | # Команда uniq требует наличия # упорядоченного списка. uniq 35.20 uniq -с | # Подсчет повторений слова. sort +0nr +ld I # Упорядочение списка по убыванию частоты # повторений, а затем — по алфавиту. -435.17 pr -w60 -A -h "Concordance for $*" # Вывод в 4 столбца. Версия сценария, имеющаяся на компакт-диске, существенно отличается от настоящей. В частности, она использует возможности утилит sed и awk. Вторая команда tr в приведенном сценарии (с опциями -cs) предназначена для Berkeley- версии этой команды. Для версии System V содержащая ее строка должна выглядеть так: tr -cs "[a-z'" "[\012*]п Если вы не знаете, какая у вас версия команды tr, обратитесь к параграфу 35.11. Вместо этой команды можно также использовать команду deroff (29.10). Одной из приятных особенностей небольших сценариев, подобных рассматриваемому, является возможность их изменения. Если, скажем, вы хотите, чтобы слово с дефисом типа copy-editor учитывалось как одно, добавьте дефис в выражение tr -cs. Например: [a-z] '- (в System V), -a-z' (в Berkeley). - JP, TOR 29.08 Поиск удвоенных слов Одной из типичных ошибок, которые трудно обнаружить при вычитке текста, является дублирование слов. Вам, по-видимому, приходилось сталкиваться с ситуацией, когда одна строка заканчивается, предположим, словом "если", а следующая этим же словом начинается. Нам известно о существовании flwfc-сценариев, обнаруживающих дублирование слов, но нет ничего проще, чем приведенная ниже функция. Здесь вы видите два ее варианта; второй из sh_htit, них предназначен для версии команды tr в System V (З5.ну. ah kit E^tt^^9 Проверка правописания, подсчет слов и анализ текста 445
29.09 йЛ*35.20 ww () { cat $* |tr -сз "a-z"' "\012" | uniq -d; } ww() { cat $* |tr -сз "[a-z]'" " [\012*]" | uniq -d; } - ГОЛ 29.09 Поиск закрывающих скобок Обычной операцией при обработке текстов является проверка того, что парные элементы текста действительно являются таковыми. Большинство текстовых редакторов UNIX имеют возможность проверять наличие закрывающих скобок в профаммах на С. Намного хуже обстоит дело с проверкой правильности структуры таких текстовых документов, как исходные файлы для профаммы troff (43./з). Например, таблицы должны начинаться с макроса . TS, а заканчиваться макросом . ТЕ. Если в HTML-документе список начинается тегом <UL>, то заканчиваться он должен тегом </UL>. В UNIX имеется целый ряд средств, которые помогают справиться с этой проблемой. Приведем сценарий, написанный Дэйлом Дохерти (Dale Dougherty), в котором для проверки парности макросов .TS и .ТЕ использована команда gawk: gawkJJ.K #! /usr/local/bin/gawk -f BEGIN { TSlineno = 0 TElineno = 0 prevFile = "" m paiKheck # Вывод результатов для очередного файла, если файлов несколько FILENAME != prevFile { if (inTable) printf ("%s: found .TS at File %s: %d without .ТЕ before end of file\n", $0, prevFile, TSlineno) inTable = 0 prevFile - FILENAME } # Поиск макроса .TS и проверка факта его нахождения в таблице /Л\.ТЭ { if (inTable) { printf ("%s: nested starts, File %s: line %d and %d\n", $0, FILENAME, TSlineno, FNR) ) inTable = 1 TSlineno = FNR ) /"Л.ТЕ { if (! inTable) ( printf ("%s: too many ends, File %s: line %d and %d\n", $0, FILENAME, TElineno, FNR) else inTable = 0 TElineno = FNR } # Конец ввода END { if (inTable) printf {"%s: found .TS at File %s: %d without .TE before end of file\n", FILENAME, TSlineno) } Сценарий такого типа можно приспособить для обработки любых файлов, но при условии, что имеются известные начальный и конечный символы. 446 Часть четвертая. Содержимое фатов
29.10 Программу более полной проверки синтаксиса можно написать с помощью лексического анализатора lex. Обычно программа lex используется более опытными программистами, работающими на С, однако ее вполне успешно может применять любой пользователь, освоивший утилиту awk и только начинающий писать на С, поскольку механизм поиска по шаблону, основанный на использовании синтаксиса регулярных выражений, здесь сочетается с функциями, написанными на более мощном и гибком языке С (см. книгу lex & уасс издательства O'Reilly & Associates.) Такого рода проблемы, конечно же, запросто решаются и посредством языка Perl (37М). - TOR 29.10 Только слова, пожалуйста Во многих видах сценариев для анализа текста часто необходимо выделять из него отдельные СЛОВа (29.08). Мне известны два способа решения этой задачи. Команда deroff разработана специально для удаления из файлов конструкций программы troff (43.13) и знаков пунктуации. Команда deroff -w выводит список только слов, содержащихся в документе. Пропустите его через команду sort -u (J6.06), если нужно оставить по одному экземпляру каждого слова. Команда deroff, однако, многое не учитывает. Она считает словом лишь ту строку символов, которая начинается с буквы. Один символ также не является для нее словом, поэтому союз "а" или "и" в список слов не войдет. Вы можете также воспользоваться командой tr (35.ri), которая способна выполнить целый рад посимвольных преобразований. Чтобы получить список отдельных слов файла, введите команду < 13.01 % tr -сз A-Za-z ■ \012' < файл Опция -с обеспечивает дополнение* строки, переданной команде tr, а опция s отвечает за удаление повторяющихся символов. Это имеет следующий смысл: "преобразовать все небуквенные символы в символ новой строки (\012)". (Было бы неплохо, если бы команда tr распознавала стандартные регулярные выражения UNIX (26.04), не так ли? Тогда вместо -с A-Za-z мы набирали бы '[AA-Za-z]'. Это выражение не менее загадочно, но, по крайней мере, используется в других программах, поэтому его не нужно будет впоследствии изучать заново.) Версия команды tr в System V (35.11) имеет несколько другой синтаксис. Тот же результат здесь можно получить, набрав % tr -сз '[A-Z][a-z]' '[\012]' < файл - TOR Имеется в вицу ввод всех символов строки, не входящих в указанный набор. — Примеч. ред. Проверка правописания, подсчет слов и аиапиз текста 447
Часть пятая Редактирование текста Во многих системах под редактированием подразумевается обработка текстов определенным образом. Как известно, современные программы обработки текстов имеют множество интересных свойств, благодаря чему изучать и использовать их достаточно просто, но в то же время многих необходимых функций они пока не выполняют. В UNIX для редактирования имеются очень мощные инструментальные средства — текстовые процессоры. Это понятие тождественно понятию "программы редактирования". Такие средства автоматизируют процесс повторного редактирования и обладают замечательной способностью выполнять за одну операцию глобальные изменения во многих файлах. Если до работы в UNIX вам приходилось иметь дело с операционной системой, оснащенной современным текстовым процессором с удобным интерфейсом, вы, по всей видимости, испугаетесь, впервые столкнувшись с редактором v/или Emacs. Но освоив такой редактор, а затем взявшись за программы типа sed и awk, вы с досадой будете вспоминать о своей прошлой высокомерности. Да, есть много особенностей, которых вам будет недоставать при работе с "реликтами" более раннего периода. В силу своих особенностей рассматриваемые программы обработки текстов до сих пор имеют много захватывающего, стоящего того, чтобы продолжать ими заниматься. - TOR
30 Особенности редактора vi 30.01 Редакторы vi и ex: зачем так много информации? Редактору vi посвящается значительная часть этой книги. Пользователи, применяющие другие редакторы, например Emacs, могут по этому поводу выразить удивление. Объясню, почему не следует удивляться. Мне приходилось встречаться со многими пользователями (к таковым отношусь и я сам), изучающими и применяющими редактор vi на протяжении лет пятнадцати. Этот стандартный редактор поставляется в настоящее время почти со всеми системами UNIX. Но большинство пользователей и не представляют, как много может делать их редактор. Когда я пытаюсь продемонстрировать им все те свойства, что присущи vi, нет предела изумлению, которое они выражают. Как уже было сказано выше, данный редактор, несмотря на некоторые свои несовершенства, относится к очень мощным инструментальным средствам. Если вы работаете с файлами, то, вероятнее всего, используете этот редактор постоянно. А умение использовать его эффективно позволит сэкономить немало времени и усилий. Но почему бы не уделить столько же внимания другому редактору, с которым работает также немало пользователей, например GNU Emacs (зш)? Да только потому, что вы можете легко получить исходный код этого редактора и расширить его, запрограммировав дополнения на языке LISP. Команды редактора GNU Emacs имеют описательные имена, которые легко понять, прочитав весь их перечень. Команды же редактора vi обычно состоят всего лишь из нескольких символов; многие варианты команд короткие и отнюдь не являются описательными. Во многих системах UNIX в настоящее время отсутствует даже исходный текст редактора vi. Надеюсь, что пользователи редактора vi почерпнут для себя из этой части книги много полезной информации. А те, кто им не пользуется, просмотрев хотя бы бегло предлагаемый материал, узнают, по крайней мере, о некоторых не совсем очевидных особенностях редактора. - JP 30.02 0 чем пойдет речь В этой главе рассказывается, как извлечь максимальную пользу из того факта, что вы приобрели редакторы vi и ex. Проработав с редактором vi на протяжении некоторого времени, вы, возможно, уже узнали о многих его свойствах. Тем не менее взгляните на перечень рассматриваемых вопросов, с тем чтобы узнать, нет ли чего-то нового для вас. • Одновременное редактирование нескольких файлов, сохранение текста в буфере, его перемещение между файлами без выхода из редактора vi (параграфы 30.04, 30.05 и 30.07). • Восстановление удаленного текста из пронумерованных буферов (параграф 30.08). • Глобальный поиск и замена по шаблону (параграфы 30.09, 30.14, 30.15, 30.17 и 30.27). • Использование режима автозамены для уменьшения времени ввода с клавиатуры (параграфы 30.31, 30.32 и 30.33). Особенности редактора vi 451 15*
30.03 • Выравнивание текста (параграф 30.37). • Выполнение команд UNIX без выхода из редактора vi, так называемая фильтрация (параграфы 30.22, 30.23 и 30.26). • -. Определение местонахождения функций и включаемых файлов с помощью команды ctags и файла tags (параграфы 30.28 и 30.29). • Установка опций редакторов vi и ex в файле .ехгс для всех редактируемых файлов или только для файлов из конкретного каталога (параграфы 30.06 и 30.18). Ввод пользователем в редакторе vi двоеточия (:) служит признаком начала команды редактора ex. Более подробная информация о редакторе ех изложена в параграфах 33.03, 33.04 и 33:05. - ЕК 30.03 Мыши против редактора vi [Материал для этого параграфа взят из заметок в Usenet (1.зз). Те, кто не знаком с работой в этой сети, могут и не знать, что при отправлении ответов на статьи в Usenet пользователи часто включают в них фрагменты предыдущих сообщений, на которые дается ответ. Используемые таким образом фрагменты называются "цитируемыми" и предваряются символом >. Иногда, как это наблюдается и в настоящем параграфе, можно увидеть цитату в цитате, на что указывают символы ». В статье, помещенной в данный параграф, сначала приводится сообщение Джона Брунера (»). Джону отвечает Пирс Уэттер (>). Крис Торек, в свою очередь, обращается к Пирсу, включая в текст своего сообщения некоторые отрывки из сообщения Джона. Крис позже назвал эту статью "бессодержательным религиозным диспутом", основываясь в значительной степени на личном мнении, а не на фактах. Но я считаю, что в ней содержатся некоторые важные моменты относительно редакторов наподобие vi: редактор, для которого требуется мышь, не всегда является самым быстрым или самым удобным для работы. Кроме того, всегда забавно читать подобные сетевые "наезды". :-) — JP] From: Chris Torec <chris%umcp-cs.uucp@BRL.ARPA> Subject: Re: Перенесение UNIX-приложений на компьютеры Macintosh Date: 16 Sep 86 09:02:17 GMT To: info-unix@brl-sem.arpa >B статье <15372@mordor.ARPA> jdb@mordor.UUCP >Джон Брунер пишет: >>Для меня намного производительнее оказалась работа с >>редактором W, чем с каким-либо другим из > Ориентированных на использование мыши редакторов, »с которыми приходилось иметь дело на >компьютерах типа Мае. В статье <981@cit-vax.Caltech.Edu> wetter@tybalt.caltech.edu.UUCP Пирс Уэттер отвечает: [много шуток по поводу статьи Джона] >С учетом моего опыта, ... это утверждение — самое веселое из всех, которые-мне >когда-либо приходилось слышать. При работе за компьютером >наиболее частым моим действием является перемещение >по файлу. Вы не убедите меня в том, что позиционирование >и щелканье мышью не выполняются быстрее, >чем суматошные действия с клавишами перемещения >курсора. Я могу убедить. Без сомнения, это гораздо быстрее, по крайней мере, для меня. (Открою небольшой секрет: я не произвожу "суматошных действий с клавишами перемещения 452 Часть пятая. Редактирование текста
курсора". Если мне необходимо переместиться с середины экрана на три строки ниже и установить курсор в конце седьмого слова, я могу ввести команду. Mjjj7E. Такая последовательность символов набирается приблизительно за полсекунды.' При работе за компьютером Sun на то, чтобы взять мышь, переместить ее указатель, сделать щелчок и снова возвратиться к клавиатуре, у меня обычно уходит около четырех секунд.) >Это правда, что можно прямо перейти на строку с заданным >номером. Но нелегко переместиться на пять строк вверх >и перейти через двадцать символов. На ввод команды 5К201 уходит примерно секунда. Проблема здесь заключается в том, чтобы быстро подсчитать количество символов перемещения. Но это приобретаемый навык, как и умение пользоваться мышью. >С помощью -редактора, ориентированного-на использование >мыши, намного легче вырезать и вставлять текст (если бы >вы увидели программу, которую я написал, вы поняли бы, >почему мне нравится следующее изречение: "Кому нужен цикл >for-next, я могу вставить его пять раз."). Скорость работы зависит от многих факторов. Действительно, иногда при работе на компьютере Sun я пользуюсь мышью — если считаю, что так будет проще и быстрее. >Вам приходится убирать руки с "любимого" ряда.клавиатуры, >чтобы нажать клавишу [ESC] или какую-нибудь другую >управляющую клавишу. Мне же не нужно делать этого. Но я вынужден выполнять много движений при работе с мышью. >Мышь вовсе не хуже (если только вы не работаете на >клавиатуре с очень большой скоростью). Правда, мне >прйдется упомянуть об одной детали: я пользуюсь не мышью, >а шаровым манипулятором (трэкболом)... Немаловажная деталь! Мне, конечно же, хотелось бы иметь в своем распоряжении клавиатуру, мышь, шаровой манипулятор, световое перо, планшетное устройство, сенсорный экран, устройство слежения за движением глаз, устройство голосового ввода. Но на самом деле при использовании устройств ввода должен соблюдаться разумный подход — едва ли целесообразно задействовать в программе все что угодно и все сразу. >Достаточно Действительно! — СТ, из телеконференции net.unix в Usenet, 16 сентября 1986 г. 30.04 Редактирование нескольких файлов Команды редактора ех позволяют во время редактирования переключаться с одного файла на другой. Выигрыш в скорости налицо! Когда вы разделяете систему с другими пользователями, требуется время, чтобы выйти и вновь войти в редактор vi в случае каждого файла, который необходимо редактировать. Если же вы остаетесь в одном и том же сеансе редактирования и перемещаетесь между файлами, то это не только ускоряет доступ к последним, но и позволяет выигрывать на определяемых вами сокращениях и последовательностях команд. Кроме того, располагая буфером обмена (yank buffer) (3o.os), вы можете копировать текст из одного файла в другой. Особенности редактора vi 453
30.06 При первом вызове редактора vi можно сразу указать несколько редактируемых файлов, а затем перемещаться между ними, применяя команды редактора ех: % vi файл! фажв2 По этой команде сначала редактируется первый файл, файл1. После окончания редактирования по команде : w редактора ех этот файл записывается (сохраняется), а по команде : п начинает редактироваться следующий файл, файл2. С помощью команды vi * вы можете задать редактирование всех файлов в каталоге. При необходимости получить имя текущего файла нажмите [CTRL-g] или задайте команду : f. Команда : args выведет имена всех файлов, которые заданы в командной строке, и поместит в квадратные скобки имя текущего файла. С помощью команды : е редактора ех можно также в любое время переключиться на другой файл, который в командной строке не указан. Если вам необходимо в редакторе vi отредактировать еще один файл, сохраните сначала текущий файл (:w), а затем наберите команду :• имя_ файла Редактор vi "помнит" имена двух файлов: текущего и альтернативного. На эти имена можно сослаться, воспользовавшись символами % (имя текущего файла) и # (имя альтернативного файла). Симюл # особенно полезен при использовании команды :е, поскольку позволяет легко переключаться между двумя файлами. Команда :е# — это всегда команда "переключиться на другой файл". (В некоторых системах команда [СТКЬ-Л] редактора vi является "синонимом" команды :е#.) Если вы вначале не сохраните текущий файл, то редактор vi не позволит переключиться на другой файл с помощью команды : е или : п (если только вы не прикажете ему сделать это безоговорочно, добавив после команды восклицательный знак). Команда :е! также достаточно полезна. Она отвергает результаты редактирования и возвращает пользователя к последней сохраненной версии текущего файла. Символ %, в противоположность символу #, полезен главным образом при записи содержимого результатов редактирования в новый файл. Например, вторую версию файла letter можно сохранить посредством команды :w %.new не набирая команды :w letter.new — LL, из книги Learning the vi Editor издательства O'Reilly & Associates 30.05 Вставка текста из одного файла в другой Когда буферу обмена присваивается однобуквенное имя, у пользователя появляется удобный способ перемещения текста из одного файла в другой. Именованные буферы не очищаются, когда по команде :е (зо.М) редактор vi загружает новый файл. Таким образом, путем копирования или удаления текста из одного файла в буфер (или в несколько именованных буферов, если это необходимо), вызова нового файла посредством команды :е и вставки содержимого именованного буфера в новый файл производится перемещение редактируемого материала между файлами. Ниже показано, как переместить текст из одного файла в другой. 454 Часть пятая. Редактирование текста
Команды "f4yy :е letter "fp 30.06 Результат With a screen editor you can scroll the page, move the cursor, delete lines, insert characters, and more, while seeing the results of your edits as you make them. Копирование четырех строк в буфер f. "practice" 6 lines 238 characters Сохранение файла. Dear Mr. Henshow: I thought that you would fee interested to know that: Yours truly, Загрузка по команде :е файла letter. Переместите курсор в то место, куда будет помещен скопированный текст. Dear Mr. Henshow: I thought that you would be interested to know that: 5£ith a screen editor you can scroll the page, move the cursor, delete lines, insert characters, and more, while seeing the results of your edits as you make them. Yours truly, Помещение ранее скопированного текста из именованного буфера f под курсором. Если вы скопируете текст в буфер и наберете имя этого буфера прописными буквами, то новый текст будет добавлен к уже находящемуся в буфере. Например, по команде "f4yy можно скопировать четыре строки в буфер, который носит имя f. Если вы затем переместитесь в какое-либо место и наберете команду "F6yy (буква F должна быть прописной), в этот же буфер f будет добавлено еще шесть строк (т.е. всего их станет десять). Вы можете копировать в буфер, имя которого задается прописной буквой, столько данных, сколько вам заблагорассудится. Когда необходимо вывести весь скопированный текст, для задания имени буфера воспользуйтесь строчной буквой (как в команде "fp). Для того чтобы очистить буфер и начать заполнение сначала, укажите его имя строчной буквой ("fy. ..) еще раз. — LL, JP, из книги Learning the vi Editor издательства O'Reilly & Associates 30.06 Локальные установки для редакторов vi и ex Наряду с файлом .ехгс, находящимся в начальном каталоге (4.щ, многие версии редактора vi будут читать одноименный файл и из текущего каталога. Это позволяет устанавливать опции, предназначенные для конкретного проекта. Так, может возникнуть необходимость иметь один набор опций в каталоге, который используется при программировании: set number lisp autoindent sw=4 terse set tags=/usr/lib/tags- а другой — в каталоге, используемом при редактировании текста: set wrapmargin=15 ignorecase Особенности редактора vi 455
3QM Обратите внимание на тот факт, что определенные опции можно установить в файле .ехгс в своем начальном каталоге и отменить их (например, set wrapmargin=0 noignorecase) в другом каталоге. Примечание: В System V Release 3.2 и более поздних версиях редактор vi не станет читать файлы .ехгс в текущем каталоге, если сначала не будет установлена опция ехгс в файле .ехгс в начальном каталоге: set ехгс Основное предназначение этого механизма — противодействие попыткам других пользователей поместить в ваш рабочий каталог файл .ехгс с командами, которые могут причинить вред системе. Можно также определить альтернативное окружение редактора vi, сохранив заданные опции в файле с другим именем, а не в файле .ехгс, и читать их с помощью команды : so. Например: :so .progoptions Отдельные файлы .ехгс также полезны для определения автоматически заменяемых сокращений (зь.31) и переназначения клавиш (ЗШ). Так, при работе над новой книгой или руководством все вводимые сокращения необходимо сохранять в файле .ехгс в каталоге, в котором этот документ создается. Установки, а также команды запуска для редакторов vi и ех можно сохранить в переменной среды (6.01), которая называется EXIN1T (зо.зз). В случае возникновения конфликта между установками в переменной EXINIT и в файле .ехгс более высоким приоритетом обладают установки переменной EXINIT. — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 30.07 Использование буферов для перемещения и копирования текста Во время сеанса работы редактора vi последнее выполненное пользователем удаление (команда d либо х), а также результат копирования (команда у) сохраняются в буфере. Получить содержимое этого буфера и поместить сохраненный текст обратно в файл можно с помощью команды put (p или Р). Нередко используется следующая последовательность команд: 5dd удалить пять строк, перейти куда-либо р вставить пять удаленных строк после текущей строки Не все знают о том, что редактор у/ сохраняет девять (зо.щ последних удалений в пронумерованных буферах. Пользователь может получить доступ к любому из этих пронумерованных буферов и восстановить любое (или все) из последних девяти удалений. (Однако если удаляется лишь незначительная часть строки, в нумерованные буферы она не записывается.) Небольшие по объему фрагменты могут быть восстановлены только командой р (или Р) и сразу же после того, как удаление было произведено. Редактор vi также позволяет копировать текст в "именованные" буферы, обозначаемые буквами. Вы можете заполнить текстом до 26 буферов (с именами от а до z), а затем, во время сеанса редактирования, восстановить нужный текст в любой момент с помощью команды put. Это особенно важно при необходимости перемещать данные из одного файла в другой, поскольку содержимое всех буферов, за исключением именованных, теряется, когда пользователь начинает работать с другими файлами (см. параграф 30.05). — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 456 Часть пятая. Рецактирование текста
30.09 30.08 Восстановление удаленных данных из пронумерованных буферов Хорошо, если есть возможность удалить большой блок текста за один прием. Но как быть, если по ошибке будут удалены, предположим, 53 строки._нужного текста? Не стоит волноваться — любой из девяти удаленных последними фрагментов документа можно восстановить, так как все они сохраняются в пронумерованных буферах. Последний фрагмент хранится в буфере 1, предпоследний — в буфере 2 и т.д. Для восстановления удаленного текста наберите " (двойная кавычка), укажите номер буфера и задайте команду put. Чтобы восстановить из буфера 2 текст, удаленный предпоследним, наберите "2р Удаленный текст, который попадает в буфер 2, помещается в строке после курсора. Даже если вы не знаете, в каком именно буфере хранится нужный вам текст, повторять команду "пр несколько раз нет необходимости. Воспользуйтесь командой повторения (.), а после команды р задайте команду u (undo — отменить). Номер буфера автоматически увеличится, и вы сможете выполнить поиск в следующем из пронумерованных буферов: "lpu.u.u и т.д. Содержимое каждого буфера будет поочередно помещаться в файл. После нажатия на клавишу и восстановленный текст удаляется. Если нажать клавишу с точкой (.), восстановится содержимое следующего буфера. Продолжайте нажимать клавиши и и . (точка) до тех пор, пока не восстановится текст, который вы ищете. — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 30.09 Поиск по шаблону и глобальные команды При обращении к строкам редактор ех (включая, конечно, и режим ех редактора vi) наряду с использованием номеров строк и символов адресации (., $, %) может осуществлять поиск по шаблонам (26.oi). : /шаблон/'d Удаляет следующую строку, содержащую шаблон.. : /шаблон/+й Удаляет строку, которая следует за строкой, содержащей шаблон. (Вместо одного символа + можно использовать +1). : Iшаблон!I, /шаблон2/й Удаляет все, начиная со строки, содержащей шаблон!, и заканчивая строкой, содержащей шаблон2. :., /шаблон/т23 Перемещает в позицию после строки 23 текст, который начинается в текущей строке (.) и заканчивается в строке, содержащей шаблон. Обратите внимание на то, что шаблоны ограничиваются с обеих сторон символом косой черты. Рассмотрим в качестве примера файл, представленный ниже. With a screen editor you can scroll the. page, move the cursor, delete lines, insert characters, and more, while seeing results of your edits as you make them. Удаление по шаблону в редакторах vi и ех выполняется по-разному. Команды Резуяьтат d/while With a screen editor you can scroll the page, move the cursor, while seeing results of your edits as you make them. В редакторе vi команда "удалить до шаблона" удаляет текст, начиная с места текущего положения курсора и заканчивая словом while, не изменяя в то же время оставшиеся части обеих строк. Особенности редактора vi 457
30.10 With a of your screen edits editor as you you make can sere them. 11 the Команда редактора ex удаляет все строки из указанного диапазона (в данном случае как текущую строку, так и строку, содержащую шаблон). Эти строки удаляются полностью. Глобальный поиск В редакторе vi для поиска символов по шаблону используется символ косой черты (/). В противоположность этому редактор ех имеет команду :д, которая позволяет находить текст по шаблону и выводить на экран в процессе поиска все строки, которые содержат этот шаблон. Команда :д! является обратной по отношению к команде :д. Используйте команду : g! (или ее синоним : v), чтобы найти все строки, которые не содержат шаблон. Команду глобального поиска можно использовать по отношению ко всему множеству строк файла или указать ей пределы глобального поиска, задавав определенные строки или диапазон номеров строк. :д/шаблон/ Поиск последнего экземпляра шаблона в файле (курсор перемещается к шаблону). : д/'шаблон/'р Поиск и вывод на экран всех строк файла, которые содержат шаблон. :д! /шаблон/пи Поиск и вывод на экран всех строк файла, которые не содержат шаблон. При этом выводится и номер каждой найденной строки. : 60,124д/шаблон/р Поиск и вывод на экран всех содержащих шаблон строк, начиная с 60-й и заканчивая 124-й. Команду g можно также использовать для глобальной замены. Например, чтобы найти все строки, которые начинаются словом warning:, и заменить в них первое слово not словом NOT, необходимо выполнить команду \<\>26.04 :g/AWARNING:/s/\<not\>/NOT/ — LL, из книги Learning the vi Editor издательства O'Reilly & Associates 30.10 Подтверждение замены в редакторах ex и vi При выполнении команд поиска и замены необходимо быть предельно внимательным. Но иногда случается, что получаешь не то, что ожидаешь. Любую команду поиска или замены можно отменить командой и, которая распространяется только на самое последнее выполненное в редакторе действие. Однако часто пользователь замечает нежелательные изменения слишком поздно. Другой способ застраховать редактируемый файл — сохранить его с помощью команды :w перед выполнением глобальной замены. В таком случае файл, по крайней мере, можно закрыть без сохранения изменений и снова открыть, начав все сначала. Кроме того, прочитать предыдущую версию содержимого буфера редактора можно, воспользовавшись командой :е! (зо.щ. Вы в точности должны представлять, что именно собираетесь изменить в своем файле. Если " вам нравится следить за тем, что происходит во время поиска, и подтверждать каждую операцию замены до того, как она будет произведена, добавьте в команду замены опцию с (для подтверждения): :1,ЗОв/his/the/gc По этой команде на экран будет выводиться вся строка, в которой обнаружен заданный текст, причем последний будет помечаться серией знаков вставки (ллл): copyists at his school 458 Часть пятая. Редактирование текста
30.12 Если вы хотите выполнить замену, нажмите клавишу у (ответив yes — "да"), а затем клавишу [RETURN]. Если вы не будете вносить изменений, просто нажмите клавишу [RETURN]. this can be used for invitations, signs, and menus. AAA В редакторе vi комбинация команд п (повторить последний поиск) и . (повторить последнюю команду) также позволяет достаточно быстро пролистать файл и выполнить в нем изменения, которые не хочется производить глобально. Обнаружив, например, что вместо слова that вы используете слово which, можете проверить каждый случай его применения и произвести замену только там, где это необходимо: /which Поиск слова which. cwthat IBSCI Замена его словом that. п Повторный поиск. Повторить замену (если необходимо). • Такой способ часто оказывается более быстрым, чем использование глобальной замены с подтверждением. [Он также позволяет увидеть строки, в окружении которых находится проверяемый текст. — JP\ — DD, TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 30.11 Сохраняйте первоначальный вариант файла и записывайте изменения в новый Командой :w можно воспользоваться и при необходимости сохранить весь редактируемый файл под новым именем. Предположим, что у вас есть файл с именем practice, в котором содержится 600 строк. Вы открыли файл и внесли в него уйму исправлений. После окончания редактирования вы хотите сохранить как старую версию файла practice, так и новую, с тем чтобы позже их можно было сравнить. Для сохранения результатов редактирования в файле по имени checkjne задайте команду :w checlc_me Старая версия файла, под именем practice, останется без изменений (при условии, что ранее вы не использовали команду :w). Теперь по команде :q можно выйти из редактора, сохранив старую версию. — LL, из книги Learning the vi Editor издательства O'Reilly & Associates 30.12 Сохранение части файла В процессе редактирования иногда возникает необходимость сохранить часть первоначального варианта текста в виде отдельного файла. В частности, вы можете таким образом сохранять коды форматирования и текст, которые впоследствии будут использоваться в различных файлах в качестве "шапки". В редакторе ех существует возможность указать строки (зз.оз) в команде :w, что позволит сохранить только часть файла. Если вы, скажем, редактируете файл practice и хотите сохранить его часть под именем newfile, достаточно ввести следующие команды: :230,$w newfile Сохранить часть файла, начиная со строки 230 и заканчивая последней строкой, Под именем newfile. :.,600w newfile Сохранить часть файла, начиная с текущей строки и заканчивая строкой 600, под именем newfile. [Если файл с именем newfile уже существует, вместо команды w необходимо воспользоваться командой w! — JP[ — LL, из книги Learning the vi Editor издательства O'Reilly & Associates Особенности рецептора ю 459
золз 30.13 Добавление текста в конец существующего файла Чтобы добавить весь редактируемый текст или только его часть в конец уже существующего файла, следует указать в команде w оператор переадресации вывода (»). Например, если ввести :l,10w newfile а затем $33.0.1 :340,$w » newfile то файл с именем newfile будет содержать строки 1-10, а также строки, начинающиеся 340-й и заканчивающиеся последней строкой текста. — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 30.14 Перемещение блоков текста, заданных шаблонами Вы можете перемещать блоки текста, границы которых заданы шаблонами (зо.оу. Рассмотрим в качестве примера справочное руководство, состоящее из 150 страниц. Каждая тема в руководстве разделена на три параграфа с тремя одинаковыми заголовками: SYNTAX, DESCRIPTION и PARAMETERS. Вот как может выглядеть одна страница: .Rh 0 "Get status of named file" "STAT" .Rh "SYNTAX" .nf • integer*4 stat, retval integer*4 status(11) character*123 filename retval = stat (filename, status) . fi .Rh "DESCRIPTION" Writes the fields of a system data structure into the status array. These fields contain (among other things) information about the file's location, access privileges, owner, and time of last modification. .Rh "PARAMETERS" .IP "\fBfilename\fR" 15n A character string variable or constant containing the Unix pathname for the file whose status you want to retrieve.- You can give the... Предположим, что позже было принято решение разместить параграф SYNTAX после параграфа DESCRIPTION. Выполняя поиск по шаблону, можно переместить блоки текста на всех 150 страницах одной командой: :g/SYNTAX/,/DESCRIPTIOK/-l mo /PARAMETERS/-1 . Эта команда применяется по отношению к блоку текста, расположенному между строкой, содержащей слово SYNTAX, и строкой, непосредственно предшествующей слову DESCRIPTION (/DESCRIPTION/-1). Блок перемешается (команда то) в строку, которая находится непосредственно перед строкой со словом PARAMETERS (/PARAMETERS/-1). Обратите внимание, что редактор ех может поместить текст только после указанной строки. Чтобы дать ему указание поместить текст перед строкой, необходимо сначала переместиться на одну строку вверх, задав параметр -1, а затем вставить текст. В случаях, подобных этому, одна команда позволяет сэкономить много времени. (Этот пример взят из реальной жизни. Однажды мы воспользовались аналогичным шаблоном, чтобы переделать справочное руководство,, в котором было несколько сотен страниц.) 460 Часть пятая. Редактирование текста
3M5 Пользоваться определениями блоков с помощью шаблонов столь же удобно и в других командах редактора ex. Например, чтобы удалить из главы справочного руководства все параграфы DESCRIPTION, достаточно набрать команду :g/DESCRIPTION/,/PARAMETERS/-ld Этот очень мощный прием внесения изменений отнюдь не является очевидным даже для опытных пользователей. Поэтому, когда вам придется иметь дело со сложной задачей редактирования, потратьте время и проанализируйте проблему, чтобы найти способ, как выполнить поставленное задание с помощью средств поиска по шаблону. — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 30.15 Популярные команды глобальных изменений Механизм поиска по шаблону лучше всего изучать на примерах. Несколько таких примеров приведено ниже. (В параграфе 26.10 содержится перечень имеющихся шаблонов.) Тщательно изучите синтаксис команд, чтобы понять, как они работают. Благодаря этому вы сможете применять наши примеры в реально возникающих ситуациях. 1. Заменить все слова help (или Help) словом HELP: % 33.03 :%s/[Hh]elp/HELP/g ИЛИ :%s/[Hh]elp/\OS/g Параметр \U заменяет в стоящем после него шаблоне все строчные буквы прописными. Приведенный шаблон задает повторный поиск (help либо Help). 2. Если после двоеточия (:) или точки (.) идут один или больше пробелов, заменить их двумя пробелами (пробел здесь обозначен квадратиком): :%з/\([:.]\)Ш*/\1ПП/д Сравниваться может любой из двух символов, заключенных в квадратные скобки. Эти символы запоминаются в буфере хранения ("hold buffer) с помощью операторов \ ( и \) (З4.щ, а восстанавливаются оператором \1. Обратите внимание, что внутри квадратных скобок нет необходимости отменять назначение специальных символов наподобие точки путем ввода обратной косой черты (\). 3. Удалить все пустые строки: g 33.04 :g/A$/d Здесь шаблон задан символами начала (А) и конца ($) строки, между которыми какие-либо другие символы отсутствуют. 4. Удалить все пустые строки, а также строки, содержащие только пробельные символы: :g/A[ntab]*$/d (В приведенной команде символ табуляции представлен как tab.) Если пробелы или символы табуляции отсутствуют, строка считается пустой. Может казаться, что строка пустая, хотя на самом деле она содержит пробельные символы. Команда из предыдущего примера не будет удалять такие строки. С помощью данного шаблона проверяется, нет ли пробельных символов между началом и концом строки: :g/A[Dtab] [Dtab]$/d 5. Найти во всех макросах (43.W заголовков секций (.Ah) первый аргумент, заключенный в кавычки, и заменить все строки с макросом этим аргументом: :%s/A\.Ah "\([А,,]*\)" .*/\1/ При замене предполагается, что макрос .Ah может содержать более одного аргумента, взятого в кавычки. Необходимо запомнить в буфере все, что находится между кавычками, но только Особенности редактора vi 461
30.16 до первой закрывающей кавычки. Использование последовательности . * считается некорректным, потому что с этим шаблоном будут сравниваться сразу все аргументы в строке. Нам же необходима последовательность символов, которые не являются кавычками: [ЛП] *. Шаблон [Л,!]* позволяет найти кавычки, за которыми идет любая последовательность символов, заканчивающаяся кавычками. Поставьте перед этим шаблоном и после него знаки \ ( и \), чтобы восстановить содержимое буфера оператором \1. 6. Выполнить те же действия, что и в предыдущем примере, с той лишь разницей, что первоначальный вариант строки с макросом будет сохранен, а изменится ее копия: :g/A\.Ah/t$ I sA.Ah "\ЦА"]Л)" .*Л1/ В редакторе ех вертикальная черта (|) служит разделителем команд и используется аналогично точке с запятой (;) (s.os) в командной строке UNIX. Первая часть команды :g/A\.Ah/t$ ищет все строки с макросом . Ah. Параметр t обеспечивает копирование этих строк в конец файла, помещая их после последней строки ($). Вторая часть команды такая же, как и в предыдущем примере, за исключением того, что подстановка осуществляется в скопированные строки в конце файла. Исходные строки остаются неизменными. — TOR, DG, из книги Learning the vi Editor издательства O'Reilly & Associates 30.16 Подсчет количества слов, отключение повторного поиска Хотите узнать, сколько раз в файле встречается слово very? Это можно сделать несколькими несложными способами. Но сначала дайте редактору vi указание прекратить поиск, когда будет найден конец файла. Для этого введите команду : set nowrapscan в командной строке редактора или поместите ее в файл .ехгс (4.09). 1. С помощью команды 1G перейдите в начало файла. Найдите первое слово very, задав команду /very. (Подсказка: использование вместо этой команды регулярного выражения /\<very\> (26.04) предотвратит сравнение с такими словами как every). Чтобы найти следующее слово very, введите команду n (next — следующее). Если редактор vi выдает сообщение Address search hit BOTTOM without matching pattern (При поиске достигнут КОНЕЦ файла; соответствие шаблону отсутствует), значит, уже найдены все слова. 2. Воспользуйтесь командой :g/very/p На экране отобразится целый список строк, соответствующих шаблону. Чтобы получить к тому же и номера строк, перед началом поиска введите команду : set number. - JP 30.17 Преобразование начальных букв каждого слова строки в прописные Приходилось ли вам когда-нибудь набирать заглавие статьи, в котором все слова должны начинаться с большой буквы, или менять строчные буквы текста на прописные? Согласитесь, не очень увлекательное занятие — нажимать клавишу [Shift] при вводе текста или использовать символ ~ (тильда) и команду w при необходимости произвести изменения в тексте. Приведенная ниже команда заменяет во всех словах первые строчные буквы прописными: :sA<./\us/g У вас может возникнуть вопрос, почему мы не использовали для замены строчных букв в начале слов команду : s/\< [a-z] /\u&/g. Дело в том, что оператор <. обозначает первые символы любых слов, но изменению подвергаются только буквы (параметр \и). Поэтому, если первый символ необходимо заменять во всех словах, достаточно указать оператор <.. Приведенная выше команда относится только к текущей строке. В команде после двоеточия можно указать диапазон строк. Например, чтобы отредактировать все строки в файле, необходимо задать команду :%s/\<./\us/g 462 Часть пятая. Редактирование текста
30.19 Когда изменения нужно произвести в текущей строке и в следующих пяти, воспользуйтесь командой ..+ 5 33.03 :. ,+5s/\<./\us/g Чтобы в каждом слове сделать первые буквы прописными (параметр \и), а все остальные — строчными (параметр \l), выполните команду \( Л)..\1 26.10 : s/\<\ (. \) \ (IA-Za-г] *\) \>/\u\l\L\2/g Последняя команда не делает строчными начальные буквы слов, которые стоят после дефисов (как в слове CD-ROM) или после апострофов (как в слове O'Reilly). Объясняется это тем, что шаблон [A-Za-z]*\> задает только те слова, в которых все символы, начиная со второго и заканчивая последним, являются буквами. При желании в шаблон можно добавить дефис или апостроф — в таком случае заданное выражение будет охватывать большее количество слов. Для набора указанных команд приходится прикладывать немало усилий. Если вы часто их применяете, воспользуйтесь возможностью переназначения клавиш (3i.o2). - JP 30.18 Автоматическая установка опций редактора vi для отдельных файлов В файле .ехгс (4.ю) можно установить опции редактора vi для всех файлов или для файлов из отдельного каталога (зо.щ. Ниже перечислены другие способы задания пользовательских установок. • По-прежнему существует возможность задействовать строки режима (modelines) (зо.щ. По причинам безопасности их использование нежелательно, но при осторожном их применении проблем не должно быть. Строки режима позволяют сохранять команды установок для каждого редактируемого файла в самом файле. • Для каждого подлежащего редактированию файла вместо использования строк режима можно создать отдельные файлы установок. В этом случае отпадут проблемы, связанные с безопасностью при использовании строк режима, поскольку файлы установок можно сделать доступными только для чтения (см. параграф 30.21). • В параграфе 30.20 рассказано, как путем ввода соответствующей команды в интерпретаторе shell выбирается один из нескольких наборов установок. Все команды редактора vi, которые будут вызываться впоследствии, должны использовать эти установки. Выполнить новые установки можно в любое время. - JP 30.19 Строки режима: ошибка или полезное свойство? В некоторых версиях редакторов vi и ех присутствует опция modeline (строка режима) или modelines (строки режима). Если опция установлена в файле .ехгс (зом), команды установок могут запоминаться в начале или конце любого редактируемого файла. При запуске редактор будет читать и выполнять эти команды установок. Это очень напоминает тот случай, когда для каждого редактируемого файла имеется отдельный файл .ехгс. Использование строк режима может породить проблемы, связанные с обеспечением безопасности. Подумайте, с какими неприятностями можно столкнуться, если какой-то злобный пользователь отредактирует ваши файлы и изменит в них строки режима. Большинство новых версий редактора vi по умолчанию отменяют действие строк режима. Особенности редактора vi 463
30.20 Ниже приведен пример файла сценария интерпретатора shell (44.oi), второй строкой которого является строка режима: #! /bin/sh # vi:set number wrapmargin=0 autoindent showmatch: while read line do Строка режима начинается с # — символа комментария интерпретатора shell. Поэтому интерпретатор проигнорирует эту строку, в то время как редактор vi прочитает ее. Символ комментария следует использовать только в сценарии интерпретатора shell. Из примера также видно, что строка режима не обязательно должна начинаться с первой позиции строки. Сама строка режима состоит из пробела или символа табуляции, выражения vi: или ех:, перечня команд, которые должны быть выполнены, и закрывающего двоеточия. Важное значение имеет как присутствие в начале строки режима пробела или символа табуляции, так и наличие в конце двоеточия — эти символы указывают редактору, где начинается и заканчивается строка режима. Строки режима помещаются в первые или последние пять строк файла (либо одновременно и в начало, и в конец). Когда редактор vi запускается с приведенным выше файлом, он устанавливает опции number, wrapmargin=0, autoindent и shovmiatch. Примечайте: При каждом открытии файла со строкой режима редактор vi устанавливает для него статус "модифицированный", даже если никакие изменения в файле не производились. Для выхода из файла без его перезаписывания приходится пользоваться командой :ql. Затруднение возникает, когда применяются утилиты UNIX, оперирующие временем модификации файла, (в частности, при использовании утилиты make (28.13), особенно если к тому же установлена опция autowrile). Чтобы определить, поддерживает ли ваша версия редактора vi строки режима и называется ли эта опция modeline или modelines, получите с помощью команды :set all список всех опций. Если такая опция имеется, но не установлена, вы увидите в списке опций название nomodeline (или nomodelines). Поместите в свой файл .ехгс команду set. modeline(s) и данная опция будет установлена. К сожалению, некоторые версии редактора vi выводят эту опцию по команде :set all, но не поддерживают ее. - JP 30.20 Использование нескольких файлов установок; поиск при запуске [В'этом параграфе рассматривается редактор vi, но все, что о нем говорится, применимо и к другим редакторам, которые при запуске читают файл установок. — JP] Как и многим другим пользователям, мне при написании программ хотелось бы использовать опции редактора vi, которые отличались бы от тех же опций, применяемых при наборе обычных текстовых файлов. В этом параграфе я объясню, как этого добиться. Вместо того чтобы в каждый файл помещать строки режима (зо.щ или создавать для каждого из них файл установок (зо.н), я создаю несколько различных файлов .ехгс (зом), по одному для каждого из режимов, в которых будет использоваться редактор vi. Кроме того, я создал псевдонимы (Ю.о2) для команд, которые позволяют выбирать именно тот файл .ехгс, который мне необходим. Мой редактор vi при запуске сообщает, какой файл .ехгс используется в данный момент. Ниже приводятся строки (с комментариями) из моего файла .cshrc (2.01) (на компакт-диске содержится набор определений команд для интерпретаторов семейства Bourne shell). 464 Часть пятая. Редактирование текста
30.22 setenv EXSTAT text # ПРИСВОЕНИЕ ЗНАЧЕНИЯ. ПЕРЕМЕННОЙ О # ОПРЕДЕЛЕНИЕ ПСЕВДОНИМОВ ДЛЯ КОМАНД ИЗМЕНЕНИЯ ФАЙЛА .ехгс # cshlnit, # установить для клавиши [tab] четырехсимвольный отступ shjnit alias 4vi 'cp ~/lib/vi/exrc4 -/.exrc; setenv EXSTAT programming' # УСТАНОВИТЬ ДЛЯ КЛАВИШИ [TAB] ВОСЬМИСИМВОЛЬНЫЙ ОТСТУП ~I4.II alias 8vi ' cp ~/lib/vi/exrc8 -/.exrc; setenv EXSTAT text' setenv 6.1)1 .^ УСТАНОВИТЬ ДЛЯ РЕДАКТОРА vi РЕЖИМ БЫСТРОГО ВЫПОЛНЕНИЯ, # ЕСЛИ СИСТЕМА РАБОТАЕТ МЕДЛЕННО (БЕЗ ФАЙЛА .ехгс) alias qvi 'rm -/.exrc; setenv EXSTAT quick' .* ПСЕВДОНИМЫ ДЛЯ КОМАНДЫ vi. # ОДИН ИЗ НИХ СНАЧАЛА СООБЩАЕТ РЕЖИМ РЕДАКТОРА vi. # у* 10.0.1 alias vi 'echo "MODE: $EXSTAT"; sleep 1; /usr/ucb/vi \!*' sleep 40.02 # ВЫЗОВ РЕДАКТОРА vi И ЗАДАНИЕ ОПЕРАЦИИ ПОИСКА: alias vs '8vi; vi +/\!*' В переменной £К57у4Гзапоминается, какой файл установок был сохранен в файле .ехгс. Поскольку запустить редактор vi, дав ему указание выполнить поиск (vi +/ШБЯОЯ), но не установив опцию wrapscan, невозможно, то я запускаю команду vs, которая, в свою очередь, вызывает команду Svi. В файле ехк8, используемом при выполнении этой команды, производится установка опции wrapscan. Как это делается, можно понять из приведенного ниже примера. В данном случае редактируется файл report и в нем производится поиск строк, где есть слово misteak: % vs misteak report MODE: text "report" 45 lines, 2734 characters - JP 30.21 Отдельный набор установок для каждого файла Возникает ли у вас необходимость в том, чтобы для некоторых файлов устанавливать свои опции редактора, а не использовать один и тот же набор установок для каждого редактируемого файла? Создайте особый файл установок с тем же именем, что"и редактируемый файл, но только добавьте в конец этого имени символ подчеркивания •(_). Например, файлу, называемому report, может соответствовать файл с набором установок, который называется report_. (He следует использовать символ подчеркивания в конце имени редактируемого файла (хотя это допускается), поскольку символ подчеркивания не относится к специальным символам интерпретатора shell (я.щ.) Файл установок имеет тот же формат, что и файл .ехгс (зо.щ. Чтобы заставить редактор прочитать его, назначьте (п.ю) какой-либо функциональной, клавише, например [F1], или любому сочетанию клавиш команду source 33.04 map #1 : source % л[ "[31.06 ~ При запуске редактора vi нажмите эту клавишу, чтобы прочитать файл установок. (Знак процента используется здесь вместо имени текущего файла (зо.М).) Если есть необходимость использовать один и тот же файл установок с различными файлами каталога, между ними можно установить жесткие ссылки (ы.М). . Таким образом будет экономиться место на диске. К тому же, если вы решите изменить опции установок, то достаточно будет отредактировать файл под любым из этих имен. - JP 30.22 Фильтрация текста с помощью UNIX-команд ^м^ При работе в редакторе vi блок текста можно передать какой-нибудь команде UNIX в качестве 7^ стандартного ввода. Результаты вывода этой команды заменят первоначальный блок текста. Такую фильтрацию можно выполнить либо из редактора vi, либо из редактора ex. Однако [3023] в редакторе ех блок текста указывается с помощью номеров строк, а в редакторе vi — с помощью текстовых объектов (команд перемещения). №■#. Особенности редактора vi 465
30.22 Фильтрация текста в редакторе ех В следующем примере показано, как отфильтровать текст в редакторе ex. Предположим, что в редактируемом файле содержится список имен, которые в строках с 96-й по 99-ую следует расположить в алфавитном порядке (отсортировать). Для этого необходимо просто набрать номера строк, которые должны фильтроваться, поставить после них восклицательный знак и указать нужную команду UNIX. Так, по команде :96,99!sort строки с 96-й по 99-ую будут пропущены через фильтр sort pe.oi) и заменены результатом работы команды sort. Использование оператора % редактора ех — это самый простой способ фильтрации всех строк текста. Если редактируется, скажем, профамма на языке С, то всю ее можно подать на вход форматирующей профаммы, которая называется indent, набрав команду :%!indent Фильтрация текста в редакторе vi При фильтрации текста в редакторе vi с помощью какой-либо UNIX-команды необходимо ввести восклицательный знак (!), команды перемещения курсора, задающие блок текста, а затем UNIX-команду, которую следует выполнить. Например, если ввести !)хоманда то следующее предложение будет обработано указанной командой. Несколько слов об особенностях редактора vi, проявляющихся при использовании такого выражения. • Во-первых, восклицательный знак на экране появляется не сразу. Происходит это после того, как вводится команда перемещения курсора, необходимая для указания текстового объекта, который следует отфильтровать; символы, используемые для обозначения объекта, не должны появляться. • Во-вторых, текстовые блоки должны состоять более чем из одной строки. Поэтому необходимо пользоваться только такими командами, которые вызовут перемещение более чем на одну строку (G, { },(),[[ ]],+,-). Чтобы повторить действие команды перемещения, перед восклицательным знаком или текстовым объектом следует указать количество повторов. (Например, и команда ! 10+, и команда 10!+ указывают на следующие десять строк.) Команды перемещения, например w, не производят никакого действия, если только не задать их в таком количестве, чтобы произошел переход за пределы одной строки. Указать текстовый блок можно также, воспользовавшись косой чертой (/), после которой следуют шаблон и символ возврата каретки. В таком случае вводом для команды фильтрации служит весь текст, расположенный до шаблона. • В-третьих, существует особый текстовый объект, который можно использовать только в команде, имеющей следующий синтаксис (второй восклицательный знак указывает на Текущую строку): ! !хоманда Не забывайте, что повторение действия символа перемещения задается числом, которое указывается либо перед командной последовательностью, либо перед текстовым объектом. Например, чтобы произвести изменения в строках 96-99, как это было сделано в предыдущем примере, необходимо переместить курсор на строку 96 и ввести либо команду 4!!sort либо команду < 4!sort Рассмотрим в качестве еще одного примера фрагмент текста, который необходимо послать в телеконференцию сетевых новостей в Usenet о.зз). В Usenet текст, который может иметь 466 Часть пятая. Редактирование текста
30.23 оскорбительный характер или является ответом на вопрос, подвергается "ротации": буква а заменяется буквой я, буква b — буквой о и т.д. Для ротации текста существуют специальные программы, но ее также легко выполнить с помощью команды tr (js.ii). В приведенном ниже примере второе предложение является блоком текста, который будет отфильтрован этой командой. One With move and sentence before. a screen editor you can the cursor, delete lines scroll the page , insert more, while seeing the results as you make them. One sentence after. of characters, your edits Последовательность действий, выполняемых при ротации текста, описана ниже. Команда Результат '■ ) One sentence after. i Восклицательный знак появляется на последней строке как приглашение ввести UNIX-команду. tr '[a-m] [n-z][А-М] [N-Z]' '[n-z][a-m][N-Z][A-MJ ' One sentence before. Jvgu n fperra rqvgbe lbh pna fpebyy gur cntr, zbir gur phefbe, qryrgr yvarf, vafreg punenpgref naq zber, juvyr frrvat gur erfhygf bs lbhe rqvgf nf lbh znxr gurz. One sentence after. Вводится UNIX-команда и нажимается клавиша [RETURN]. Входной блок текста замещается выходным. Чтобы снова применить фильтр, не набирайте команду фильтрации заново. Редактор vi может повторно выполнить предыдущую UNIX-команду. Синтаксис такой операции: ! объект ! Иногда бывает полезным послать фрагменты документа команде nmffw.i.y, чтобы взамен получить отформатированный текст. Не забывайте, что первоначальный текст замещается результатом работы команды. К счастью, если будет вьшолнено некорректное действие или вместо ожидаемого результата будет получено сообщение об ошибке, действие UNIX-команды можно отменить и восстановить строки текста, выполнив команду и (команду редактора vi undo — отменить). В параграфе 30.37 показано, как с помощью фильтра можно привести в порядок строки текста. — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 30.23 Безопасные фильтры редактора vi При выполнении фильтрации в старых версиях редактора vi, содержащих ошибки, текст часто превращается в совершеннейшую несуразицу. Результат может быть настолько неудовлетворительным, что откажется работать даже команда и. Если к тому же проявить беспечность и не записать перед началом операции фильтрации результаты редактирования (посредством команды :w), то файл может быть полностью утерян! С подобной проблемой я сталкивался при работе в различных версиях редактора vi, а также не раз читал об этом в Usenet в статьях, написанных пользователями, которые также оказывались в этой неприятной ситуации. Особенности редактора vi 467
9024 Чтобы больше на этом не обжигаться, попробуйте воспользоваться возможностью переназначения клавиш (31.02). Переназначьте одну из клавиш, например функциональную клавишу [F4], на выполнение фильтрации всего файла. Для запуска процесса фильтрации нажмите клавишу [F4], что приведет к выполнению последовательности команд, указанных в первой строке. Затем наберите UNIX-команду, которую хотите выполнить. Чтобы произвести фильтрацию, нажмите клавишу [F4] еще раз. Варианты переназначения клавиш показаны ниже. Для ввода управляющих символов наберите сначала [CTRL-V1 (И.щ, а затем комбинацию клавиш [ЛМ], [AV] или [Л[]. тар #4 :se noawAM:wAM:%dAM:г ! тар! #4 "V '%'АМ:Id"[:se awA[ В первой последовательности (тар #4) нажатие клавиши [F4] в командном режиме приведет к установке опции noautowrite (команда :se noaw), записи содержимого буфера редактора в файл пользователя (:w), удалению из этого буфера всех строк (:%d) и вызову приглашения интерпретатора shell (: г ! (33.04)). После нажатия в командном режиме клавиши [F4] в нижней строке появляется курсор, что служит указанием ввести название фильтра:. :г ! Введите UNIX-команду (наподобие expand, fmt -75). Затем опять нажмите, клавишу [F4]. Поскольку вы находитесь в режиме ввода текста^ использоваться будет второе из.приведенных переназначений (шар! #4). В последовательности переназначений вначале идут комбинация клавиш [CTRL-V] и пробел. Благодаря этому приему между набранной пользователем командой- и именем файла помещается пробел. Затем выводится имя текущего файла (обозначается как ^ (зому), помещенное в одинарные кавычки (чтобы интерпретатор shell не обработал специальный символ), а за ним следует символ возврата каретки, указывающий на конец ввода команды. При вводе UNIX-команды по приглашению :г ! остается пустая строка сверху; команда : Id уничтожает эту строку. Наконец, команда :se aw снова включает опцию autowrite. Если вы не хотите устанавливать опцию autowrite, то можете исключить команды :se noawAM и :se awAM в переназначении клавиш. Но не сомневайтесь, что все строки в вашем файле будут удалены, если при использовании данного переназначения клавиш установлен режим автозаписи! (Пустой буфер редактора будет автоматически записан после вызова интерпретатора shell.) Этот сложный вариант переназначения клавиши ни в одной из версий редактора v/ не работает достаточно безупречно. В моей старой версии редактора, предназначенной для операционной системы SunOS 4.1, этот прием не позволял выйти из режима ввода текста — после второго нажатия клавиши [F4] приходилось нажимать еще и клавишу [ESC]. Тем не менее в версиях редактора vi, содержащих ошибки, этот способ намного надежнее, чем обычная фильтрация. Особенно он хорош тем, что всегда имеется возможность отменить результаты фильтрации, выполнив команду и или : е!. Я не использовал это переназначение уже несколько лет, но фильтры редактора vi для меня являются таким важным инструментом, что я всегда буду держать их под рукой. - JP 30.24 Восстановление файлов в редакторах vi и ex и сетевые файловые системы Приходилось ли вам использовать команду vi -r для восстановления файла? Эта команда позволяет восстановить файл, который вы редактировали, когда в операционной системе произошел аварийный сбой или какая-либо другая неприятность, из-за чего редактор был выгружен из памяти и файл не сохранился. В таком случае система выдает почтовое сообщение (из), подобное приведенному далее. 468 Часть пятая. Редактирование текста
30.26 Date:. .Tue, 19 Nov 91 09:59:00 EST To: jerry Копия буфера редактора вашего файла afile была сохранена при выходе системы из строя. Буфер можно получить обратно по команде редактора recover. Чтобы добиться этого, введите команду vi -r afile.1 Эта команда выполняется также в редакторах edit и ex. .,» Ваши файлы, очевидно, сохраняются в каталоге, имеющем название наподобие /usr/preserve. ^^S*< Следуйте указаниям, и вы получите свой файл обратно примерно таким же, каким он был ^В в момент аварийного завершения работы. ПОРЧ1 ^Ри использовании сетевой файловой системы, например NFS, команда vi -r будет работать ' ' не совсем корректно. Причем выполняться она может только на том компьютере, где редактировался файл. Например, если файл foo редактировался на компьютере, который называется artemis, и там произошел аварийный сбой, вы не сможете зарегистрироваться на другом компьютере и выполнить команду vi -r foo для восстановления файла. Объясняется это тем, что на многих компьютерах временные файлы (например, буферы редакторов) сохраняются не в сетевых (совместно используемых) файловых системах, а в локальных. В системах такого типа, чтобы восстановить потерянный буфер редактора, необходимо зарегистрироваться именно на компьютере artemis. Если вы не помните, каким компьютером пользовались, когда файл был потерян, просмотрите строки в разделе Received: (Получен) в заголовке сообщения электронной почты. Там часто указывается, какая машина была первоначальным отправителем сообщения. Вы также можете запросить список файлов, имеющих резервные копии, введя команду vi -r без имени' файла: % vi -г /var/preserve/jerry: On Wed Jul 17 at 08:02 saved 15 lines of file "/u/jerry/Mail/drafts/1" On Sun.Aug 25 at 18:42 saved 157 lines of file "doit" /Imp 21.02 /tmp: .. No files saved. He откладывайте восстановление файлов надолго. Большинство UNIX-систем удаляют сохраненные буферы редакторов не реже одного раза в месяц или неделю, а иногда и чаще. - JP 30.25 Команда vi -r и сохранение файла после выхода из редактора Обычно, когда файл редактируется в редакторе vi, то после ввода команды zz он успешно сохраняется. Но в некоторых версиях этого редактора, когда файл восстанавливается командой у/ -г (зо.24), ввод команды ZZ не всегда приводит к его сохранению. Но в этом может быть свой положительный момент. При восстановлении буфера необходимо решить, является ли полученный буфер действительно тем, который вам нужен. Возможно, после того печального момента вы уже успели произвести какие-то изменения в файле или при сохранении буфера произошла ошибка (скажем, случился аварийный сбой в системе). Не следует сохранять файл, не проверив его. Убедившись, что восстановлены все нужные данные, смело записывайте эту версию файла с помощью команды :w!. Если восстановленная версия файла вам не нужна, воспользуйтесь для выхода из редактора командой : q!. - JP 30.26 Временный выход в интерпретатор shell Некоторые UNIX-команды, например интерактивные команды редактора vi, позволяют пользователю во время их выполнения временно запускать другие команды. Чтобы сделать это, следует ввести особый символ команды (обычно таковым является восклицательный знак) и задать необходимую команду. В настоящем параграфе я продемонстрирую это на Особенности редактора vi 469
30.27 примерах, воспользовавшись редактором vi. Чтобы проверить, работает ли данный метод с другими утилитами, просмотрите документацию по ним или просто попытайтесь набрать ! команда, когда утилита ожидает от вас ввода команд. Не выходя из редактора vi, можно выполнить любую команду UNIX. Это удобно, в частности, при необходимости прочитать почту или просмотреть файлы, а затем возвратиться в то место файла, где редактирование было прервано. Данная операция называется временным выходом в интерпретатор shell. (Кстати, это же можно сделать другим способом, который называется управлением заданиями (n.oi) и применяется во многих UNIX-системах с большинством интерпретаторов. На мой взгляд, управление заданиями является способом более традиционным и гибким, чем временный выход в интерпретатор shell.) Предположим, вы редактируете файл foo, содержащий телефонные номера. Чтобы найти нужный номер, следует воспользоваться командой grep. Для этого вы должны выполнить ряд действий. 1. Убедитесь, что вы находитесь в командном режиме (нажмите клавишу [ESC], если не уверены в этом). 2. Если вы хотите выполнить команду, для которой нужен редактируемый файл, сначала сохраните буфер редактора vi с помощью команды :w. (В случае применения команды grep, приведенной ниже, в этом нет особой необходимости.) Введите команду : !, затем укажите команду и нажмите клавишу [RETURN]. Например: ' 14.11 : 'grep tin» -/phone 3. Начнет выполняться команда grep. Когда она завершится, редактор vi выдаст следующее сообщение: [Hit return to continue] (Для продолжения нажмите [RETURN]) После того как будет нажата клавиша [RETURN], редактирование продолжится с того места, где была вызвана команда grep. Приведем еще несколько примеров команд. :! рд файл Постраничный просмотр файла на экране. : Iptroff % Обработка файла программой форматирования ptroff, редактор vi заменяет параметр % именем редактируемого в настоящий момент файла. : Imail Чтение почты. Будьте осторожны с этой командой, если программа mail уже выполняется и из нее для редактирования сообщения по команде ~v вызван редактор vi. При таком способе выхода в интерпретатор shell будет создан порожденный процесс рям), который не вернет вас в прежний сеанс работы с программой mail. : sh Запуск нового интерпретатора shell. У вас почти всегда будет возникать желание для временного останова редактора vi (п.м> использовать управление заданиями, если интерпретатор shell поддерживает такую возможность. По существу, при временном выходе в интерпретатор shell можно делать то же самое, Что и в обычной строке приглашения shell. Однако интерпретатор shell при этом выполняется в порожденном процессе. Таким образом, команды типа cd не действуют на ту программу, из которой был запущен порожденный процесс. И что отрадно, изменение каталога или какие-либо другие изменения среды не окажут воздействия на редактор vi или тот интерпретатор shell, из которого редактор был вызван. После завершения программы, выполняемой в порожденном процессе, вы вернетесь в то место, где находились до вызова. - JP 30.27 Комбинированный поиск в редакторе vi Вы, вероятно, знаете, что найти слово или выражение можно, задав в редакторе vi команду / (косая черта): /txmeure 470 Часть пятая. Редактирование текста
36.27 При необходимости перейти к какому-то конкретному месту, где находится определенное слово, которое в данном файле используется множество раз, команду поиска п следует повторять до тех пор, пока нужное место не будет найдено. Предположим, вам нужно найти слово treasure в предложении, которое выглядит приблизительно так: Los Alamos residents. . . treasure, но вы не можете воспроизвести это предложение дословно. Данную проблему можно решить, воспользовавшись в регулярном выражении до.м; метасимволами: /Los Alamos. * treasure Но для этого необходимо, чтобы выражения Los Alamos и treasure находились в одной и той же строке файла. А так бывает не всегда. Кроме того, необходимо, чтобы курсор находился на слове treasure, однако он во время поиска будет помещен на слове Los. "Гм-м, — скажете вы. — Может, попробовать применить две разные операции поиска?" /Los Alamos /treasure Но проблема состоит в том, что в файле полным-полно выражений Los Alamos. Вам придется вводить команду п снова и снова, пока не будет найдено предложение со словом treasure. Простой способ выхода из этого положения — выполнение составного поиска. Допустим, курсор находится в первой строке следующего файла; Before the second World War, there was a treasured boys' school in what was to become the city of Los Alamos, New Mexico. The school at Los Alamos changed the lives and made a lifelong impression on most boys who attended. One of the boys who attended Los Alamos school went on to propose that remote set of mesas as a site for the U.S. Government's Since the war ended, most of the boys' school ranch buildings have, been torn down or replaced. But there's one building that Los Alamos residents still use and treasure. It's The Lodge, a log building on the edge of what's now Введите команду /Los Alamos/;/treasure/ которая означает следующее: "Найти первое слово treasure, идущее после выражения Los Alamos". В процессе поиска, который запускается с начала файла, будут пропускаться все слова treasure и Los Alamos, пока очередь не дойдет до слова treasure, находящегося в последней строке. (Вероятно, более разумным было бы ввести команду /Alamos/,- /treasure/ — на случай, если слова Los и Alamos попадают в разные строки файла.) Другой пример. Программист, пишущий программы на языке С, хочет найти вызов функции print/сразу после строки, где переменная / увеличивается на 2 (i += 2). Для этой цели можно воспользоваться командой /i += 2/:/pxint£/ Примечайте: Составной поиск невозможно задать повторно командой п. Самый простой способ его организации — переопределение клавиши ошу. "М 31.06 :map g /Los Alamos/;/treasure/AM и нажатие клавиши g (в данном примере) для повторения операции. - JP Особенности редактора vi 471
30.28 30.28 Отслеживание функций и включаемых файлов с помощью команды ctags и файла tags Обычно исходный текст больших программ на С занимает несколько файлов. Иногда бывает трудно определить, в каком файле находится определение конкретной функции. Решить эту проблему можно путем использования команды ctags одновременно с командой : tag (ярлыки) редактора W. Команда ctags создает информационный файл (базу данных), используемый позже редактором vi для поиска в файлах определений функций. По умолчанию этот файл называется tags. В нем содержатся строки с такими полями: идентификатор файл контекст где идентификатор — имя функции или макроса на языке С, файл — файл, в котором определены данные функция или макрос, контекст — шаблон для поиска строки исходной программы, содержащей вызов функции или макроса. Задаваемая в редакторе vi команда .:! 30.26 : ! ctags иыя_файл$. с создает файл tags в текущем каталоге пользователя. Файл tags — это база данных, содержащая информацию о функциях, определенных в файле имя_файла.с. Команда вида' :!ctags *.с создает файл tags, содержащий описание всех файлов с исходными программами на С в текущем каталоге. [Если вы будете использовать файл fogs, находясь в каком-либо другом каталоге, не забудьте указать в команде полный путь, например: '...'9.16 r'ctags 'pwd'/*.c • По этой команде полные путевые имена (Ш2) будут сохранены в файле tags. — JP\ Теперь предположим, что файл tags содержит информацию о всех исходных файлах, используемых для сборки С-программы. Предположим также, что вы хотите посмотреть или отредактировать функцию в программе, но не знаете, где эта функция находится. Введенная в редакторе vi команда :tag имя_функцюг позволяет просмотреть файл tags, с тем чтобы найти имя файла, содержащего определение функции имя__функции. Затем редактор загрузит этот файл и установит курсор на строку, в которой содержится определение данной функции. При этом нет необходимости знать, с каким файлом ведется работа; следует только решить, какую именно функцию нужно редактировать. Я предпочитаю работать с файлом tags следующим образом; помещаю курсор на первой букве имени функции, а затем нажимаю клавиши {CTRL-]]. Редактор vi прочитает файл tags и покажет на экране то его место, где определяется функция, на имя которой указывает курсор.— JP] Примечание: Причиной того, что файл tags не создается, возможно, является установка для вашего редактора vi опции nowrapscan. Такая проблема существует во многих версиях редактора v/. Если команда :set wrapecan устраняет проблему, связанную с созданием файла tags, внесите ее в свой файл .ехгс (4.оя) или переменную EXINIT (б.оз). (За эту подсказку благодарю Криса Торека.) — JP, из книги Learning the vi Editor издательства O'Reilly & Associates 472 Часть пятая. Редактировать текста
30.31 30.29 Задание нескольких файлов tags Файл tags можно хранить в своем текущем каталоге. Но можно также иметь другой файл tags, общесистемного или общегруппового характера. Как же заставить редактор vi просматривать оба файла? В файле .ехгс ром) или в командной строке редактора после двоеточия (:) введите обратную косую черту (\) между именами файлов tags: set tags=tags\ /usr/local/lib/tags - JP 30.30 Проблемы с клавишей Esc в редакторе vi [Следующая подсказка связана с терминалом VT220 компании Digital Equipment, но сама идея, возможно, будет полезна и в отношении других терминалов. — JP] Я работал в университете, лаборатории которого были оснащены терминалами VT220. Такие терминалы могут работать и в режиме VT220, и в режиме VT100. К сожалению, клавиша [ESC] посылает символ ESC только тогда, когда терминал находится в режиме VT100. В режиме VT220 она выдает символ ESC "в сопровождений" еще четырех символов ([23-), что заставляет редактор, vi подавать звуковой сигнал и переключать регистр вводимых символов: Эту проблему можно решить, поместив в файл .ехгс ром) команду тар, [23- ram В таком случае каждый раз при нажатии клавиши [ESC] в режиме VT220 будет генерироваться символ ESC, а символы [23- будут отвечать за выполнение в редакторе vi команды mm. Данная команда отмечает (зо.з8) текущее положение курсора символом m — ничего полезного, но редактор vi, по крайней мере, "замолкает". - JP 30.31 Режим автозамены в редакторе vi В редакторе vi можно определять сокращения, которые при наборе текста будут автоматически заменяться полным текстом. Чтобы задать такие сокращения,, воспользуйтесь следующей командой редактора ex. :ab сокр виражанив где параметр сокр — это сокращенный вариант задаваемого выражения. Последовательность символов, образующих сокращение, заменяется при вводе только в том случае, если набирается как отдельное слово. [Аналогичным образом я поступаю, когда произвожу замену слова Covnex словом Convex — такого рода ошибки часто возникают при быстром вводе текста. — ТС\ Предположим, необходимо ввести текст, отдельные слова или фразы которого часто повторяются (в частности," это могут быть названия изделий или компаний). Приведенная далее команда :ab ns the Nutshell Handbook вводит сокращение ns для выражения the Nutshell Handbook. Теперь всякий раз, когда при наборе текста вы будете вводить ns как отдельное слово, эти буквы будут заменены соответствующим текстом. Сокращения заменяются сразу же после того, как вводится неалфавитный символ* (например, разделительный знак), символ возврата каретки или символ ESC (возврат в командный режим). Выбирая сокращение, используйте комбинацию символов, которая обычно не * Сокращение не заменяется при вводе знака подчеркивания (_), который интерпретируется как часть сокращения. Особенности редактора vi 473
30:92 применяется при наборе текста. Если сокращение встречается в таком месте, где оно не должно заменяться, отмените операцию замены, набрав команду :unab сокр Чтобы получить список сокращений, определенных в данный момент, введите :аЬ Символы, которые входят в сокращение, не должны появляться в конце выражения, замещающего сокращение. Например, если вы зададите команду :ab PG This movie is rated PG то получите сообщение No tale recursion (Запрещена рекурсия в конце выражения), и сокращение не будет установлено. Это сообщение означает, что вы пытались определить что-либо, что приводит к повторной замене самого себя, создавая бесконечный цикл. Если задать команду :ab PG the PG rating system то можно получить, а можно и не получить бесконечный цикл, но в любом случае предупреждающее сообщение выдано не будет. Так, при проверке последней команды в TJNIX версии System V замена происходила, но в версии Berkeley сокращение повторно заменялось следующим образом: the the the the the ... Это повторялось до тех пор, пока не возникала ошибка памяти и редактор vi не прекращал свою работу. Мы советуем избегать повторения сокращений как части определяемого выражения. — DD, DG, из книги Learning the vi Editor издательства O'Reilly & Associates 30.32 Использование режима автозамены для обмена файлами Команда а£ (зо.зо редактора vi предназначена в первую очередь для сокращения слов. Но ее можно применять и для сокращения часто используемых команд режима ex. Причем для этих команд (начинающихся двоеточием) сокращение может оказаться даже более удачным подходом, чем переназначение клавиш (зи>2), так как пользователь может выбрать практически любое название команды, не беспокоясь о возможных противоречиях с существующими командами редактора vi. Приведем конкретный пример. При наличии терминала, позволяющего работать с окнами, или же имея несколько терминалов, вы можете запускать несколько копий редактора vi. Система будет поддерживать методы переноса текста между окнами, но более простым и приемлемым иногда оказывается способ, когда используются файлы из каталога /tmp (21.02), предназначенного специально для манипулирования большим объемом текстовой информации. Ниже перечислены некоторые сокращения, применяемые мной в файле .ехгс (4.ояу. Hab aW w! /tmp/jerry.temp.a 'ab aR r /tmp/jerry.temp.a ab bW w! /tmp/jerry.temp.b ab bR r /tmp/jerry.temp.b - Я иепользую эти сокращения следующим образом. Чтобы записать текущую и последующие 45 строк во временный файл а, я набираю в одном из сеансов работы с редактором vi команду 7. ,+45 аК Для чтения сохраненных таким образом строк в другом сеансе работы с редактором vi предназначена команда :аК Вы можете сделать то же самое во время одного сеанса работы с редактором vi, воспользовавшись именованными буферами (зом), но задействование временных файлов — это единственный метод обмена данными между различными сеансами работы. - JP 474 Часть пятая. Редактирование текста
30.34 30.33 Автозамена часто встречающихся опечаток Автозамена (зол) является удобным методом исправления опечаток общего характера. Попробуйте применить в своем файле .ехгс (зо.М) несколько сокращений типа ab teh the ab taht that В любой момент, обнаружив, что вы переставляете буквы или ругаете себя примерно следующими словами: "Болван, снова ошибка в этом слове", — добавьте в файл .ехгс соответствующее сокращение. (Конечно, не следует забывать и о производительности — такой файл не должен быть слишком большим.) Сокращения можно применять и для выполнения действий более общего характера. Например, при создании книги имена команд набирались курсивом. Но воспользовавшись сокращением наподобие ab vi \fIvi\fP мы сэкономили на вводе большого числа кодов программы troff (43.13). Это сокращение не является рекурсивным (зол), поскольку подстрока vi помещается между другими алфавитно-цифровыми символами, а не располагается отдельно как слово. - TOR 30.34 Команды работы со строками и символами в редакторе vi [Очень мало пользователей редактора vi понимают, как создаются для него команды, имеющие синтаксис (число)(команда)(текстовый объект). Немногие понимают и разницу между строковыми и символьными командами. Мы поговорим об этом в настоящем параграфе, приведем некоторые примеры. — JPJ Команда _ (символ подчеркивания) очень похожа на команду л (знак вставки), поскольку она перемещает курсор на первый непробельный символ текушей строки. Основная разница между ними состоит в том, что команда _ работает со строками, а команда Л — с символами. Это важно для всех функций (или команд), которые читают "адрес" текстового объекта, например d, у или с. Команды delete (удалить), yank (скопировать) и другие вызывают в редакторе W стандартную подпрограмму для обработки адреса. Если это адрес отдельного символа, редактор выполняет удаление или копирование в режиме работы с символами. Если это адрес строки, редактор выполняет операцию в режиме работы со строками. "Адресной" командой может быть любая стандартная команда позиционирования (например, W, Ь, $ или /шаблон/) либо введенная дважды исходная символьная команда (dd или уу). Некоторые из таких команд представлены в табл. 30.1. Таблица 30.1. Примеры строковых и символьных команд редактора vi Команда Назначение dd Удаляет текущую строку d' а Удаляет все строки между текущей строкой и строкой, содержащей маркер а включительно da Удаляет все символы между текущим символом и символом, отмеченным маркером а. Выполнение этой команды во многом напоминает выполнение команды Ли редактора Emacs, в котором также считается, что начало и конец удаляемого диапазона символов находятся между двумя заданными символами. Обратите внимание, что команда удаления символов может удалять и символы перевода строки Особенности редактора vi 475
30.35 Команда Назначение c/accord/ c?accord? yj УН <G • ! )fmt Изменяет все без исключения символы (не строки!), находящиеся между текущим символом и словом accord, без учета самого этого слова, (см. следующую команду) - Изменяет все символы между текущим символом и словом accord, включая и само это слово Копирует две строки — текущую и следующую за ней Копирует все строки, начиная с первой строки на экране и заканчивая текущей Сдвигает влево строки, расположенные между текущей строкой и последней включительно. (Переменная shiftwidth определяет, на сколько позиций производится сдвиг.) Обратите внимание, что эта команда преобразует адреса символов в адреса строк (аналогичную операцию выполняет и подкоманда >) Прогоняет строки, расположенные между текущей строкой и концом абзаца, через программу fmt (зол) Примечание: , Если установлена опция wrapscan, выполнение команды поиска вида c?accord? может вызвать переход от конца файла к его началу, т.е. привести к неожиданным результатам. Именно по этой причине я использую в своем- файле .ехгс команду set nows. К сожалению, выключение опции wrapscan во многих версиях редактора vi является причиной создания файла tags с неправильной структурой. Если для заданной символьной команды имеется счетчик повторений, то редактор vi объединяет его значение со значением счетчика повторений для команды перемещения, поэтому команда 2y2j копирует пять строк. Интересно, что команда 2у2_ копирует четыре строки (то же делает и команда 2у2у), поскольку команда _ перемещает курсор на строку вниз (значение счетчика повторений уменьшается на 1). Будьте осторожны и не используйте счетчик повторений со всеми командами перемещения, так как не все они выполняются одинаковым образом. Команда 4 $ перемещает курсор в конец третьей строки (счет начинается со строки, идущей после текущей); команда 4Л просто перемещает курсор на первый непробельный символ текущей строки. Команда I (вертикальная черта) является синонимом команды 0 (нуль); если задан счетчик повторений, то она вызывает перемещение курсора на соответствующее число символов вправо от начала строки (как если бы было задано | (счетУик_ловторений-1) 1. (Вопрос, для читателя: "Почему нельзя задать счетчик повторений равным О?") Команды, введенные прописными буквами, выполняются по-другому. Как именно — зависит от конкретной команды. Точные действия не всегда могут казаться имеющими смысл, но обычно эти команды выполняют операции с текущей строкой: D действует .как d$, С — как с$, a Y — как у$. Это необходимо просто запомнить или же пользоваться хорошим справочным руководством. — СТ, из телеконференции net.unix в Usenet, 19 марта 1984 г. 30.35 При нехватке памяти для временных файлов воспользуйтесь другим каталогом Редактор vi хранит временную копию редактируемого файла в одном из каталогов временных файлов (21.02). Обычно это каталоги /Imp, /usr/tmp или /var/tmp. Если редактируется большой файл или файловая система заполнена, редактор vi не всегда создает временный файл. Когда это происходит, необходимо воспользоваться командой set directory редактора vi, что позволит задать другое путевое имя для каталога временных файлов. (Если ситуация не 476 Часть пятая. Редактирование текста
30.36 изменится, необходимо обратиться к системному администратору и проверить, нельзя ли "почистить" выделенный каталог или увеличить размер файловой системы.) Вам в первую очередь потребуется узнать полное путевое имя (им) каталога, в котором есть достаточно свободного места (24.09). Воспользуйтесь существующим каталогом или создайте новый. Для определения каталога можно воспользоваться командой редактора vi set directory=/usrl/jim/vitemp Причем ввести ее необходимо до того, как редактору будет задано имя файла, который должен редактироваться. В противном случае редактор vi создаст временный файл, и переустанавливать каталог для временных файлов будет поздно. Но если ввести команду переустановки каталога, когда редактор vi уже запущен, а затем набрать команду :е (жщ, то при работе со всеми временными файлами будет использоваться новый каталог. Команду установки временного каталога, возможно, удобнее присвоить переменной среды EXJNIT (6.01): setenv EXINIT 'set directory=/usrl/jim/vitemp' Есть и другой способ изменения переменной EXJNIT {см.. также параграф 6.10). Если у вас уже есть файл .ехгс (зо.щ, установка переменной EXINIT заставит редактор vi проигнорировать его. Чтобы команда set directory возымела действие наряду с установками файла .ехгс, воспользуйтесь командой | (вертикальная черта), как показано в нашем примере: setenv EXINIT 'source /usrl/jim/.exrc | set directory=/usrl/jim/vitemp' - JP 30.36 Открытый режим редактора ex может оказаться удобным Большинству пользователей не нравится во время запуска редактора w получать сообщения наподобие Visual needs addressable cursor or upline capability [Using open mode] (Для отображения информации на экране требуется адресуемый курсор или возможность работы в экранном режиме [Открытый режим] Пользователи обычно недоумевают: "Как получается, что комбинация #&@! переводит редактор в полноэкранный режим?" Если вы один из таких пользователей, то не торопитесь с выводами. Вам понравится открытый режим редактора ех\ Прежде чем продемонстрировать возможности такого режима, напомню, что его очень удобно применять при работе с медленно функционирующими коммутируемыми линиями или сетевыми соединениями, когда подолгу приходится ждать обновления изображения на экране. Этот режим полезен и при работе в оконной системе, когда редактор vi не представляет, сколько строк помещается в окне. Чтобы быстро отредактировать файл, проще на минуту переключиться в открытый режим, не пытаясь настроить свое окно. Работа в открытом режиме напоминает работу редактора vi на экране, имеющем всего одну строку. Однако этот режим отличается от режима построчного редактирования других редакторов UNIX, например erf, или стандартного режима редактора ех, когда приглашение выдается в виде двоеточия. В открытом режиме используются обыкновенные полноэкранные команды редактора vi и только применительно к одной строке. Например, чтобы выйти из редактора, следует, как ив полноэкранном режиме, ввести команду ZZ. Если у вас уже есть какой-то опыт работы с редактором vi, самый простой способ узнать, что такое открытый режим, — это перейти в него и попытаться поработать. Имеется три способа перехода в открытый режим. Особенности редактора vi 477
30.36 • Перейдя в режим ввода команд редактора ех и получив приглашение в виде двоеточия, наберите команду :орйп и нажмите клавишу [RETURN]. •. При медленном сетевом соединении вы можете запустить открытый режим из командной строки. В зависимости от применяемой версии редактора w воспользуйтесь одной из следующих команд: % ех +ореп имя_файла % ех -с open имя_файла • Если вы уже находитесь в полноэкранном режиме редактора vi, переключитесь в открытый режим путем ввода команды Q, и вы получите приглашение редактора ех в виде двоеточия. Затем наберите команду open и нажмите клавишу [RETURN]. При переходе в открытый режим редактор выводит на экран текущую строку и помещает курсор в ее начало. Команда Результат ex +open afile [Using open mode] "afile" 47 lines, 1943 characters In the beginning, there was a cursor. Файл afile обрабатывается в открытом режиме. Обратите внимание, что строка сообщений, которая в редакторе vi выводится в самой последней строке, в данном случае отображается первой. Затем выводится первая строка файла. Курсор находится в начале строки. 2w [Using open mode] "afile" 47 lines, 1943 characters In the beginning, there was a cursor. Команда 2w редактора w перемещает курсор на два слова вперед. [Using open mode] "afile" 47 lines, 1943 characters In the beginning, there was a cursor. The screen was blank and without characters. Команда j редактора vi перемещает курсор вниз на одну строку. В некоторых версиях редактора эта команда напечатает только несколько первых символов новой строки, предшествующих той позиции, где находится курсор. Поэтому вместо команды j я обычно использую клавишу [RETURN]. Это позволяет получить на экране всю строку и переместить курсор в ее начало. [Using open mode] "afile" 47 lines, 1943 characters In the beginning, there was a cursor. The screen was blank and without characters. In the beginning, there was a cursor. Команда к редактора vi перемещает курсор вверх на одну строку, предыдущую. Это открытый режим, поэтому предыдущая строка "прокручивается" вверх. Чтобы не возникало путаницы, помните, что в текущий момент редактируется та строка, в которой, есть курсор. (Как и на предыдущем этапе, команда к может не печатать всю строку. В этом случае более подходящей является команда -.) 478 Часть пятая. Редактирование текста
30.37 [Using open "afile" 47 In the mode] lines, beginning, The screen In the end, 1943 characters there was a cursor. was blank and without characters. 'there was a cursor. После ввода символов cw вы перейдете в режим ввода текста редактора vi, в результате чего слово beginning будет заменено словом end. Нажмите клавишу [ESC] при необходимости вернуться в командный режим. Для работы в открытом режиме может понадобиться определенный навык. Но познакомившись однажды с идеей такого режима, вы найдете ее заслуживающей изучения. - JP 30.37 Выравнивание текста Встречались ли вы с ситуацией, когда после редактирования одни строки становились слишком короткими, а другие — слишком длинными. В подобном случае "подчистить" текст вам поможет утилита £nt (35.02). Предположим, после редактирования файла (сообщения электронной почты или чего-либо другого) в редакторе vi строки получились неровными. Они могут выглядеть приблизительно так: This file is a mess with some short lines and some lines that are too long — like this one,- which goes on and on for quite a while and etc. Let's see what 'fmt' does with it. Вы помещаете курсор на первую строку и вводите (в командном режиме) 5!! 30.22 5! ! fmt что означает "отфильтровать (Ж22) 5 строк фильтром fmf\ После выполнения команды строки приобретут такой вид; This file is a mess with some short lines and some lines that are too long — like this one, which goes on and on for quite a while and etc. Let's see what 'fmt' does with it. Это самый простой способ форматирования абзацев. Поместите курсор на первую строку абзаца и введите команду (в командном режиме) !} fmt Если в файле не содержится текст, который необходимо сохранить таким, каким он есть, привести весь файл в порядок можно с помощью команды % 33.03 :%! fmt Существует несколько версий утилиты fmt, одна причудливее другой. Много полезной информации представлено в статьях о различных инструментальных средствах редактирования (см. главу 35). Например, утилита recomment (35.М) по-новому форматирует блоки комментариев к программе; утилита cut (35.14) может удалять столбцы, поля или слишком короткие строки; команда tr (3s.ii) выполняет различные виды форматирования текста. Чтобы привести в порядок столбцы, попробуйте отфильтровать файл, выполнив установки, о которых рассказывается в параграфе 35.22. Если утилита способна читать данные со стандартного ввода и направлять преобразованный текст на стандартный вывод, ее можно использовать как фильтр редактора vi. - JP Особенности редактора vi 479
30.38 30.38 Как найти предыдущее место редактирования с помощью команды undo Часто во время редактирования файла приходится переходить в другую его часть, с тем чтобы возобновить в памяти отдельные детали, отменить или внести какую-то правку. Как же в таком случае можно вернуться в предыдущее место? Это место можно пометить с помощью команды т. В командном режиме введите т, после чего укажите любую букву. (Мы будем использовать в примере букву х.) Ниже приводятся команды, которые выполняют требуемые действия. тх Помечает текущую позицию маркером х. 1 х Перемещает курсор на первый символ строки, помеченной маркером х. 'х Перемещает курсор на символ, помеченный маркером х. Перемещает курсор в позицию, где последний раз выполнялось редактирование или был поставлен маркер. '' Перемещает курсор в начало строки, где последний раз выполнялось редактирование или был поставлен маркер. Но часто для отмены последнего действия редактора бывает достаточным ввести команду и. Данная команда возвращает пользователя именно в то место, где он перед этим работал, и отменяет сделанные исправления. Чтобы восстановить результат редактирования, необходимо опять ввести команду и. (Я по-прежнему использую команду т, если необходимо отметить сразу несколько правок.) - TOR
31 Создание пользовательских команд в редакторе vi 31.01 Не вводите лишнего Под переназначением клавиш подразумевается возможность задать, а затем вызвать нажатием одной клавиши сложную последовательность команд. Переназначение — один из признанных методов экономии времени. Это непередаваемое чувство, когда нажимаешь одну клавишу, а на экране происходит целый ряд изменений. Для процесса редактирования, где достаточно много повторяющихся операций (например, операций изменения шрифта), такая возможность имеет важное значение. В данной главе мы поговорим о том, как: • сэкономить время, используя принцип переназначения клавиш (параграфы 31.02, 31.04, 31.07 и 31.08); • определить, следует ли применять переназначение клавиш (параграф 31.03); • переназначить клавиши типа [ESC] и [RETURN] (параграф 31.06); • перемещаться по файлу, не выходя из режима ввода текста (параграфы 31.12 и 31.13); • вставить без изменений текст из других окон (параграф 31.05); • поместить пользовательские команды в файл .ехгс (параграфы 31.09, 31.10 и 31.14); • осуществить разбиение длинных строк текста (параграф 31.16). - ЕК 31.02 Экономия времени с помощью команды тар При редактировании файлов часто приходится применять длинные или просто очень сложные последовательности команд. Чтобы сэкономить свой труд и не тратить время на запоминание этих последовательностей, свяжите их выполнение с задействованием неиспользуемых клавиш. Воспользуйтесь для этой цели командами тар и тар!. Переназначение клавиш в командном режиме Команда тар во многом напоминает команду яй <зо.31), но в отличие от последней макроопределение здесь задается для командного режима, а не для режима ввода текста. (В режиме ввода текста используется команда тар!.) тар х последовательность Назначает символу х последовательность команд редактирования. unmap х Отменяет назначение для символа х. тар Выводит список символов (клавиш), которые в настоящий момент переназначены. Создание пользовательских команд в редакторе vi 16 9-171 481
31.02 Как и другие команды редактора ех, команды переназначения клавиш могут помещаться в файл .ехгс (4.щ или вводиться в командной строке после двоеточия. Если клавиши переназначаются только на время текущего сеанса редактирования, может оказаться, что проще воспользоваться ©-функциями pi.oy редактора vi. Команду тар целесообразно применять для переназначения клавиш в том случае, если они помещаются в файл .ехгс и используются во многих сеансах редактирования. Прежде чем приступить к переназначению клавиш, вы должны запомнить те клавиши, которые не используются в командном режиме и которыми можно распоряжаться в командах переопределения. Перечислим их. Буквы: д, к, q, v, v Управляющие клавиши: ЛА, Лк, Л0, Лт, лю, лх Символы: _, *, \, = (символ = используется редактором vi, когда установлен режим Lisp) С помощью команд тар можно создавать как простые, так и сложные последовательности команд. В качестве примера определим команду для перестановки слов. Пусть при работе в редакторе vi курсор в тексте располагается следующим образом: you can the scroll page Последовательность dwwP поместит слово the после слова scroll: dw означает удалить слово, w — переместиться на следующее слово, Р — поместить удаленное слово перед курсором. (Вместо w можно использовать W). Запомнив последовательность map v dwwP вы во время сеанса редактирования можете в любой момент переставить два слова одним нажатием клавиши v. Переназначать можно и определенные последовательности символов. Попробуем выполнить такую операцию, выбрав первый символ последовательности из приведенного выше списка доступных символов. Пусть, например, при нажатии клавиш *s слово берется в одинарные кавычки ('слово'), а при нажатии клавиш *d — в двойные ("слово"): "[3I.0t map *s Еа,Л[В1'Л[ тар *d Еа"л[Вд."л[ Теперь вы сможете выполнять сотни таких переназначений (хотя, вероятно, в вашей версии редактора v; на их количество имеется ограничение). Множество примеров переназначений приведено в параграфе 31.09. Необходимо уметь связывать последовательности переназначения с функциональными клавишами, если эти клавиши определены в базах данных termcap и terminfo (5.02). В приведенном ниже примере функциональная клавиша [F1] используется для перестановки слов: тар #1 dwelp И последнее замечание. Варианты переназначения вовсе не ограничиваются задействованием множества' неиспользуемых клавиш. Вы можете переназначить и те клавиши, которые определены как дополнительные команды редактора vi, но тогда станет недоступным первоначальное их назначение. Наверняка все будет хорошо, если клавиша "привязана" к редко используемой команде. Подробнее об опции погетар рассказано в параграфе 31.14. Переназначение клавиш в режиме ввода текста Команда тар! во многом напоминает команду тар, но используется в режиме ввода текста. Подается она в командном режиме, как и обыкновенная команда тар: после приглашения в виде двоеточия вводится команда тар!, за которой следуют пробел и обозначение той клавиши, которая переопределяется. Затем указывается пробел и тот текст, который будет подставляться в режиме ввода текста. Процесс переназначения для режима ввода текста во многом напоминает автозамену с помощью сокращений (зол). Отличие заключается лишь в том, что команда тар! позволяет переключиться из режима ввода текста в командный режим, 482 Часть пятая. Редактирование текста
3102 выполнить там команды и снова вернуться в режим ввода текста. Чтобы перейти в командный режим во время выполнения команды тар!, необходимо определить символ ESC, нажав комбинацию клавиш [CTRL-v], а затем клавишу [ESC] (лов). В таком случае после выполнения в командном режиме требуемых действий можно войти в режим ввода текста, задав команды а, i и т.д. Предположим, что в обычных условиях вы никогда не пользуетесь клавишей [Л] в режиме ввода текста. Но при наборе текста вам вдруг приходит мысль, что вводимая информация является важной и это следует подчеркнуть. Вы нажимаете клавишу [Л], после чего редактор vi должен вставить новую строку над текущей и поместить в нее выражение THIS IS IMPORTANT:. Затем редактор vi должен снова вернуть вас в режим ввода текста и поместить курсор в конец той строки, где была нажата клавиша [А]. Чтобы выполнить эти действия, войдите в командный режим и наберите приведенную ниже команду тар!. Первый символ Л — это результат нажатия клавиши [Л]. Символы Л [ получены в результате последовательного нажатия клавиш [CTRL-v] и [ESC]. Завершается переназначение нажатием клавиши [RETURN]: :map! Л Л[OTHIS IS IMPORTANT:л[JA Что делает эта конструкция? Она выполняет те же действия, что и команды редактора vi, которые используются для вставки указанных трех слов вручную. В режиме ввода текста задействование клавиши [Л] обеспечит выполнение следующих действий. 1. Нажатие клавиши [ESC] вызовет переход в командный режим. 2. Команда О вставит новую строку над текущей строкой (в режиме ввода текста). 3. Будет введен текст THIS IS IMPORTANT:. 4. Нажатие клавиши [ESC] вызовет возврат в командный режим. 5. Будет выполнена команда j — команда перехода на строку ниже (в строку, где была нажата клавиша [Л]). б. Будет выполнена команда А, в результате чего курсор переместится в конец строки (в режиме ввода текста). Однако следует отметить, что команду тар! можно использовать лишь для переопределения клавиш, не применяемых в режиме вюда текста. Чтобы в этом режиме временно отменить переназначение клавиши, нажмите клавиши [CTRL-v], а затем — нужную клавишу. Например, чтобы поместить в файл настоящий символ Л, нажмите клавиши в следующей последовательности: [CTRL-v] [Л]. Для того чтобы отменить переназначение до конца сеанса редактирования, наберите команду : unmap! и укажите после нее символы, переназначение которых отменяется. Более общим является пример переназначения клавиш со стрелками и функциональных клавиш, предназначенных для выполнения каких-либо действий в режиме ввода текста. Эти клавиши порождают специальные последовательности символов. Обычно такие последовательности помещаются в буфер редактора, как если бы вы сами набирали их. Например, на моем компьютере клавиша с изображением левой стрелки выдает символы ESC, [ и D. Если для этой трехсимвольной последовательности не определена команда тар!, то при ее появлении редактор vi приходит в полнейшее замешательство." Многие разработчики UNIX предлагают переназначения для клавиш со стрелками. Вы увидите их в списке переназначений режима ввода текста, который можно получить, задав команду :тар! без параметров: up Л[[А л[ка down Л[[В A[ja left A[[D "[hi right Л[[С л[1а л[OTHIS IS IMPORTANT:A[jA Перечень проблем, которые возникают при использовании команды тар!, приведен в параграфе 31.03 - JP, DG, LL * Действительно, символ ESC переключит редактор vi назад в командный режим. Первый символ, [, заставит его предположить, что пользователь собирается вводить команду перемещения к разделу — [ [. Поэтому следующий символ, D, заставит редактор vi подать звуковой сигнал. Безобразие, не правда ли? Создание пользовательских команд в редакторе vi 16* 483
31.03 31.03 Что вы теряете при использовании команды тар! В старые добрые времена (когда буханка хлеба стоила пять центов, а мой дедушка был еще мальчишкой) клавиши с изображением стрелок в редакторе vi в режиме ввода текста не задействовались. При перемещении по файлу приходилось нажимать клавишу [ESC] для перехода в командный режим, а уже в этом режиме пользоваться командами типа 5к и 4w. С тех пор многие разработчики и пользователи занимались модификацией редактора vi, так что теперь можно применять клавиши с изображением стрелок и в режиме ввода текста. По сути, в параграфах 31.02 и 31.13 показано, как пользователь может сделать это самостоятельно. В наши дни кое-кто считает, что новшества, внесенные в редактор vi, упрощают работу. Мы же приведем некоторые соображения в подтверждение того, что клавиши со стрелками следует оставить в покое и делать все по-старому. • В большинстве случаев команда u (undo — отменить) будет бессильной после выхода из режима ввода текста, поскольку переназначения клавиш с изображением стрелок выполняют некоторые скрытые команды. Единственной командой "отменить", которая действует более надежно, является команда U. Она отменяет все изменения, произведенные в текущей строке. Но если вы после внесения изменений переместитесь на другую строку, то и эта команда не будет работать. • Новички должны запомнить, что vi — это редактор, требующий переключения в разные режимы, что текст вводится в режиме ввода текста, изменения производятся в командном режиме, а перемещения по файлу задаются командами. Когда пользователи начинают применять редактор vi и обнаруживают, что некоторые команды перемещения (клавиши со стрелками) функционируют в режиме ввода текста, им кажется, что редактор vi лишен логики. • Если при переназначении клавиш с помощью команды тар! выполняются команды, которые начинаются символом ESC (а это почти всегда так),' то клавиша [ESC] может начать функционировать гораздо медленнее. Объясняется это тем, что всякий раз после нажатия клавиши [ESC] редактор vi в течение примерно секунды бездействует, так как ему необходимо убедиться, что это отдельное нажатие, а не начало переназначенной последовательности. (Правда, некоторые разработчики устранили такое явление.) В подобной ситуации можно нажать клавишу [ESC] дважды, хотя терминал при этом подаст звуковой сигнал. - JP 31.04 ©-Функции Команда шар (З1.ш) редактора vi позволяет переопределять назначение клавиш — присваивать командам (одной или нескольким) короткие названия. Переназначение клавиши можно выполнить во время редактирования файла в редакторе vi, введя команду :тар. Но если вы сделали ошибку, то обычно для ее исправления приходится всю команду :тар вводить заново. ©-Функции (произносится как "эт-функции") предоставляют другой способ определения сложных команд. Всего можно определить 26 ©-функций с именами от @а до @z. Запоминаются они в именованных буферах po.os). Поэтому при использовании именованных буферов для копирования и вставки текста вы должны учитывать, что в них находятся и определения ©-функций. Определение и использование простых @-функций Чтобы определить ©-функцию, вы должны выполнить действия, перечисленные ниже. 1. Введите команду (или команды), которую хотите выполнить, в одну или несколько строк редактируемого файла. 2. Скопируйте эти строки в именованный буфер посредством команды типа "ау$ или "bD. 3. Чтобы воспользоваться функцией, введите команду типа @а или @Ь. Функцию можно выполнить повторно, введя @@ или точку (.). Чтобы отменить изменения, сделанные ©-функцией, воспользуйтесь командой и или U. 484- Часть пятая. Редактирование текста
31.04 Приведем пример. Предположим, что мы редактируем длинный HTML-файл со строками наподобие таких: <STR0NG>Kd3KO#-To заголовок/ STR0NO <STRONG>#pyro# заголовок*./STR0NO Увидев одну из этих строк, вы решаете заменить тэги STRONG тэгами НЗ и Н4. Глобальная подстановка с помощью команды :%з не позволит справиться с этой задачей, поскольку в одни строки нужно подставить НЗ, а в другие — Н4. Решение необходимо принимать в каждом конкретном случае, просматривая файл строка за строкой. Поэтому определим функцию @а для замены тэга STRONG тэгом НЗ, и функцию @Ь — для замены этого же тэга тэгом Н4. Прежде чем приступать к созданию ©-функции, подумайте, как бы вы стали вносить изменения вручную. По-видимому, вы бы переместились в начало строки по команде О, сдвинулись на один символ вправо с помощью команды 1, ввели бы команду cw, чтобы изменить слово STRONG, и ввели бы НЗ (или Н4), а затем для возврата в командный режим нажали бы клавишу [ESC]. Потом вы перешли бы в конец строки по команде $, с помощью команды Т/ переместились бы на символ, находящийся после косой черты, и изменили бы второе слово STRONG таким же образом, как и первое. Чтобы определить данную функцию, вставьте в файл (в текстовом режиме) пустую строку. Затем нажмите те клавиши, которые приведут к замене текста тэгом НЗ (перед каждым нажатием клавиш [ESC]. и [RETURN1 нажимайте клавиши [CTRL-vl (л.об)). Выполнив это, нажмите еще раз клавишу [ESC], и вы перейдете в командный режим. Поскольку для замены текста тэгом Н4 используются аналогичные команды, самый простой способ создания командной последовательности — копирование команд (для этого следует ввести команды уу и р) и редактирование этих копий. Пара командных строк должна выглядеть следующим образом (символы Л[ означают последовательность клавиш.[CTRL-v][ESC]): 01сиНЗЛ[$Т/сиНЗЛ[ 01сиН4л[$Т/сиН4л[ Перейдите в начало первой строки и удалите ее в буфер я с помощью команды "aD. Перейдите на следующую строку и наберите команду "bD. (После этого останутся две пустые строки; удалите их с помощью команды dd, если хотите.) Теперь после ввода команды @а будет выполняться последовательность команд, предназначенных для замены текста в строке тэгом НЗ, а после ввода команды @Ь — для замены текста тэгом Н4. Пройдитесь по всему файлу (возможно, с использованием команды поиска /STRONG ... п ...), набирая при этом команды @а и @Ь. Для повторного выполнения изменения, производимого в предыдущей строке, можно воспользоваться командой @@. Комбинирование @-функций ©-Функция может вызывать другие ©-функции. Ниже в качестве примера приводятся четыре строки, которые можно сохранить как функции с именами из диапазона of @а до @d: 01@с$Т/@с ...будет функцией @а 01@d$T/@d ...будет функцией @Ь сюНЗЛ[ ...будет функцией @с сиН4л[ ...будет функцией @d Определение функции @а дважды включает вызов функции @с. При вызове функции @а будет выполнена команда 01, позволяющая переместиться на второй символ строки, затем команда @с, производящая замену слова гэгом НЗ, и, наконец, произойдет переход в конец строки и еще раз будет выполнена функция @с Вызов из ©-функции других ©-функций позволяет экономить на наборе повторяющихся команд. Но здесь возникает одно неудобство: команда @@ не всегда работает так, как вы предполагаете. Если вы набираете команду @а, чтобы сделать изменение в одном месте, а затем перемещаетесь куда-нибудь в другое место и набираете команду @@, то выполняется команда @с, а не @а. Происходит это потому, что команда @а завершилась выполнением команды @с. Создание пользовательских команд в редакторе vi 485
1.05 Повторное использование определения Удалить строку с определением в буфер можно не только с помощью команды dd. Если вы считаете, что хорошо отлаженная команда вам еще понадобится, скопируйте ее в буфер с помощью команды "ау$. Потом, если команду придется пересмотреть, достаточно будет по-новому отредактировать строку и пересмотренную команду снова поместить в буфер, задав "ау$. При необходимости скопировать исправленную строку в другой буфер вы также можете использовать команду "Ьу$. Новые строки в ©-функции Определение ©-функции может занимать несколько строк. Например, если вы удалите следующие четыре строки с помощью команды "z4dd, то при вводе команды @z под текущей строкой будут вставлены (команда о) четыре новые строки текста: oThis is the new line one. This is the new line two. This is the third line. This is the fourth.Л[ После выполнения функции @z курсор переместится на строку, которая находится под четырьмя новыми. Почему? Потому что вы включили в буфер символы новой строки (RETURN), а каждый символ RETURN приводит к перемещению на одну строку вниз (не исключая и самого символа RETURN после последнего символа ESC). Возможно, вы не хотите, чтобы так происходило? В таком случае можно поступить следующим образом. • Занесите первые три строки, включая и символы новой строки, в буфер, задав команду "z3dd. Удалите четвертую строку без символа новой строки и добавьте ее в тот же буфер с помощью команды "ZD. (Задание прописной буквы z, как в данном случае, вызывает добавление удаляемого текста в именованный буфер. Команда D удаляет всю строку, за исключением символа новой строки.) В некоторых версиях редактора vi в результате использования команды "z4D удаляются четыре строки без последнего символа новой строки. • Кроме того, вы можете набрать весь текст в виде одной строки и включить в нее символы новой строки, введя [CTRL-v][RETURN] между отдельными строками. Это будет выглядеть примерно так: oThis is the new line one.AMThis is the new line two. AMThis is the third... Занесите эту длинную строку в буфер с помощью команды "zD. Поскольку команда D не удаляет последнего символа новой строки, после выполнения функции @z курсор останется в конце четвертой строки, именно на этом символе. - JP 31.05 Переназначения клавиш для вставки текста в окно, в котором запущен редактор vi В системах типа X Window или Macintosh я обычно работаю с редактором vi в одном из окон. Оконные системы позволяют переносить текст между окнами. Но вставка текста в окно редактора vi может оказаться непростым делом, если установлены такие опции этого редактора, как wrapmargin или autoindent (вставляемый текст иногда смещается самым непредсказуемым образом). Такое положение можно исправить путем переназначения клавиш. Если вставляется текст, который должен копироваться точно, не подвергаясь каким-либо изменениям, я перехожу в режим ввода текста и нажимаю клавиши [CTRL-x]. Это приводит к отключению опций autoindent (noai) и wrapmargin (wm=0). Сделав вставку, я нажимаю клавиши [CTRL-n] и отменяю установки команды, выполненной посредством клавиш [CTRL-x]. 486 Часть пятая. Редактирование текста
31.06 Совершенно по-другому вставка производится при использовании клавиш [CTRL-r]. Данное сочетание клавиш во время вставки текста вызывает утилиту,^ (35.02), с тем чтобы по-новому отформатировать строки. Чтобы воспользоваться этим методом, перейдите в режим ввола текста и нажмите клавиши [CTRL-r]. Затем вставьте текст. Утилита fmt прочитает, но не станет отображать его на экране. Нажмите клавишу [RETURN], а затем [CTRL-d], и ввод данных в утилиту fmt будет закончен. Отформатированный текст появится на экране. " Установить режим "точного" ввода, что позволит в точности вставлять " фрагменты текста: тар! ЛХ n[:se noai wm=0"Ma " Установить режим "нормального" веодэ с обычными опциями wrapmargin и " autoindenL: map! "N A[:se ai ит=8лМа " Отформатировать вставленный текст с помощью утилиты fmt. Когда операция " будет выполнена, нажать [CTRL-d] : map! ~R л[:r!fmt*M Отметим, что в некоторых оконных системах символы табуляции при выполнении копирования и в процессе вставки текста преобразуются в пробелы. Если эти символы необходимо вернуть обратно, попытайтесь отфильтровать (зо.22) текст при помощи утилиты unexpand (24.06). - JP 31.06 Защита клавиш от их интерпретации редактором ех Обратите внимание, что при переопределении некоторых клавиш, например [RETURN], [ESC], [BACKSPACE], [DELETE], чтобы сделать их частью переопределяющей команды, простого нажатия недостаточно, поскольку эти клавиши уже имеют значения в редакторе ex. Если вы хотите, чтобы одна из этих клавиш стала частью командной последовательности, отмените обычное ее назначение, предварив ее появление комбинацией клавиш [CTRL-v]. Символ возврата каретки после нажатия [CTRL-v] отображается как ЛМ, символ ESC — как Л[, символ BACKSPACE — как Лн и т.д. С другой стороны, если вы хотите переназначить комбинацию клавиш (например, ЛА), в большинстве случаев достаточно нажать алфавитную клавишу, удерживая одновременно нажатой клавишу [CTRL]. Чтобы переопределить, скажем, комбинацию клавиш ЛА ([CTRL-a]), необходимо набрать следующее: :map |CTRL-aj последовательность Существует, однако, несколько символов, появление которых должно предваряться символами Av. Примером таких символов может служить ЛТ. К ним также относятся: • символы, которые используются системой для стирания части введенного в командной строке текста (ЛЯ — для стирания слов, Ли — для стирания строк (см. параграф 9.02)); • символы для прерывания заданий (38Щ и останова заданий (12Щ. Поэтому, чтобы переназначить, скажем, символ Лт, следует набрать :map lCTRL-v| ICTRL-Ll последовательность Сочетание клавиш [CTRL-v] применимо ко всем командам редактора ех, а не только к командам переназначения. Это означает, что символ возврата каретки можно использовать и для целей автозамены (МЛ). В частности, сокращение :аЬ 123 oneAMtwoAMthree раскрывается как one two three (Последовательность [CTRL-v][RETURN] показана в таком виде, в каком она появляется на экране компьютера: Лм.) Создзние попьзовэтепьских комзнд в редакторе и 487
31.07 Можно также добавлять строки в отдельные позиции по всему файлу. Команда :g/ASection/s//As you recall, inAMS/ вставляет перед строками, которые начинаются со слова Section, новые строки с приведенной фразой. Использование параметра & приводит к восстановлению шаблона поиска. Вертикальная черта (I) служит разделителем для нескольких команд редактора ех, поэтому ее Первоначальное назначение отменить особенно сложно. Поскольку переназначение интерпретируется как при запоминании, так и при выполнении, мы должны вставить достаточное количество символов Av, чтобы защитить вертикальную черту от каждой попытки интерпретации. Защитить необходимо и символы AV, которые используются в переназначении, добавляя перед каждым из них еще по одному символу Av. Хуже обстоит дело с переназначениями в режиме ввода текста (map! (зш>). В этом случае, прежде чем будет введена вертикальная черта, нужно трижды задать символ Av, что означает ввод целых шести символов Л\/. Например, следующее переназначение позволяет вставить строку {х|у} с помощью функциональной клавиши [F1] (зшу. map! #l {xAvAvAv|y} Если вы запросите вывод списка всех назначений, активизированных в режиме ввода текста, то увидите, что был запомнен только один символ Л\Л шар! fl : [OP {x"V|y} — LL, DG, JP, из книги Learning the vi Editor издательства O'Reilly & Associates 31.07 Переназначения для повторяющихся команд редактирования [По-другому переназначение можно произвести с помощью ©-функций (ЗШ). — JPJ Информацию не обо всех переназначениях клавиш необходимо сохранять в файле .ехгс. В некоторых случаях просто возникает необходимость повторного выполнения операций редактирования. Разработка команд для переназначения клавиш может занять немало времени, но сколько времени это сэкономит вам в дальнейшем! Предположим, вы работаете со словарем, текст которого отформатирован следующим образом: map - an ex command which allows you to associate a complex command sequence with a single key. Вы хотите преобразовать какую-либо статью словаря, задав для нее формат утилиты nroff (43.1зу. .IP "map" 10n An ex command which allows you to associate a complex command sequence with a single key. Самый простой способ определения сложного переназначения состоит в том, чтобы один раз выполнить действия по редактированию вручную, записывая данные обо всех производимых нажатиях клавиш, а затем воссоздать эти нажатия клавиш в виде переназначений. Для этргр придется выполнить следующие действия. 1. Вставить в начале строки макрос форматирования абзацев (.IP). Вставить также первую кавычку (I.IP "). 2. Нажать клавишу [ESC], чтобы завершить режим ввода текста. 3. Переместиться в конец первого слова (е) и добавить вторую кавычку, за которой следует пробел и указывается размер отступа (а" 10п). 4. Нажать клавишу [RETURN], чтобы вставить новую строку. 5. Нажать клавишу [ESC], чтобы завершить режим ввода текста. 6. Удалить дефис и два окружающих его пробела (Зх) и сделать в следующем слове первую букву прописной (~). 488 Часть пятая. Редактирование текста
31.08 Эту рутинную работу необходимо повторять много раз. Операции переназначения позволяют экономить немало времени, так как целый ряд действий вызывается одним нажатием клавиши: map g I.IP "А[еа" 10пАМЛ[Зх~ (Чтобы переопределить клавишу во время работы редактора v/', сначала нужно ввести двоеточие.) Обратите внимание, что нажатием клавиш [CTRL-v] (и.щ необходимо предварять как нажатие клавиши [ESC], так и нажатие клавиши [RETURN]. Последовательность символов Л [ появляется, когда происходит нажатие клавиш [CTRL-v], а затем [ESC]. Последовательность ЛМ указывает на то, что поочередно были нажаты клавиши [CTRL-v] и [RETURN]. Теперь обыкновенное нажатие клавиши g будет приводить к выполнению всей серии операций редактирования. При низкой скорости вывода на экран можно будет видеть, какое действие когда, выполняется; при большой скорости вывода все произойдет как по мановению волшебной палочки. Не смущайтесь, если первая попытка переназначения клавиш закончится неудачей. Незначительная ошибка в определении переназначения может привести к совершенно иному результату, нежели вы ожидали. Введите команду и, чтобы отменить результат редактирования, и попытайтесь сделать все правильно. [Я предпочитаю создавать команды переназначения во временном файле и изменять их там до тех пор, пока они не станут работать корректно. Все происходит следующим образом. Я записываю файл и набираю команду :е temp.so, чтобы открыть временный файл temp.so, а затем заношу в него команды переназначения. Сохранив этот файл с переназначением (:w), загружаю его посредством команды : so % (зо.М).* Если нет ошибок, я переключаюсь на первоначальный файл (:е #; (зо.м;) и пытаюсь проверить действие переназначенных клавиш. Затем я, как правило, снова возвращаюсь в файл с переназначением (:е#), устраняю ошибки :-), повторяя все, пока не получу положительного результата. — JP\ — TOR, из книги Learning the vi Editor издательства O'Reilly & Associates 31.08 Цругие примеры переназначения клавиш в редакторе vi Несколько советов относительно того, как целесообразнее использовать сокращения при переназначении клавиш. 1. Переход в режим добавления текста при каждом перемещении в конец слова можно организовать с помощью команды map e еа В большинстве случаев единственной причиной перемещения в конец слова является необходимость добавить (ввести) текст. Данная команда переназначения автоматически устанавливает для пользователя режим ввода текста. Обратите внимание, что переназначенная клавиша е имеет значение в редакторе vi. Допускается переназначать клавиши, которые уже используются в редакторе vi, но обычные функции этих клавиш будут недоступными до тех пор, пока эти переназначения действуют. В данном случае это не так уж плохо, поскольку команда Е часто бывает идентичной команде е. В последующих примерах предполагается, что клавише е назначена команда еа. 2. Сохранить файл и начать редактирование следующего файла из заданной в командной строке последовательности (зом) позволит команда map q :wAM:nAM Обратите внимание, что клавишам можно назначать команды редактора ех, но не забывайте, что ввод каждой ex-команды должен заканчиваться нажатием клавиши [RETURN]. Такая последовательность команд позволяет легко переключаться с одного Команда so означает, что необходимо загрузить файл и выполнить содержащиеся в нем команды. — Примеч. ред. Создание попьэовательских команд в редакторе vi 489
31.09 файла на другой и полезна в первую очередь тогда, когда вы, вызывая редактор vi, открываете одновременно много коротких файлов. Выбор символа q обусловлен тем, что последовательность команд напоминает команду quit (выйти). 3. При необходимости окружить текущее слово кодами выделения полужирным шрифтом (УВ и \fP) утилиты troff рекомендуем воспользоваться командой map v i\fB"[e\fPA[ В этой команде предполагается, что курсор находится в начале слова. В первую очередь устанавливается режим ввода текста, после чего задается код для полужирного шрифта. (В команде шар нет необходимости дважды нажимать клавишу [\] для получения одной обратной косой черты.) Возврат в командный режим происходит по нажатию клавиши [ESC], которому предшествует нажатие клавиш [CTRL-v] (31.06). Затем в конец слова вставляется закрывающий код утилиты troff и снова осуществляется переход в командный режим. Конечно, переназначение клавиш не следует использовать только для вставки кодов управления шрифтом утилиты troff. Его можно применять и для заключения слова в круглые скобки или символы комментариев языка С, и это лишь некоторые из возможных областей использования этого приема. Последний пример показывает, что последовательность переназначения может содержать другие команды (команда е уже переназначена как еа). Возможность использовать вложенные последовательности команд переназначения контролируется опцией remap (31.14) редактора vi, которая обычно бывает включенной. 4. Чтобы окружить текущее слово кодами выделения полужирным шрифтом утилиты troff, даже если курсор не находится в начале слова, можно воспользоваться командой тар V 1Ы\£ВЛ[е\£РЛ[ Данная команда похожа на приведенную в предыдущем примере. Но в отличие от таковой здесь используется команда 1Ь, позволяющая справиться с дополнительной задачей позиционирования курсора в начале слова. Если курсор находится посередине слова, может возникнуть желание переместиться на его начало с помощью команды Ь, но если он уже находится в начале слова, то команда b переместит его на предыдущее слово. Для предотвращения такой ситуации используется команда 1, выполняющая смещение на один символ. В таком случае курсор никогда не будет располагаться на первой букве слова. Вы можете создать свои варианты этой последовательности команд, заменив Ь на в и е на Еа. Тем не менее команда 1 никогда не позволит последовательности переназначения выполниться, если курсор будет находиться в конце строки. (Чтобы обойти эту проблему, добавьте пробел в конце слова, прежде чем выполнять команды переназначения.) — DG, из книги Learning the vi Editor издательства O'Reilly & Associates 31.09 Полезные установки для файла .ехгс [Возможно, вам и не понадобятся все эти переназначения и сокращения. Если к этому не вынуждают обстоятельства, я не любитель заниматься переназначением клавиш — предпочитаю запоминать настоящие команды, а не назначенные им комбинации клавиш, действующие только при условии, что я зарегистрировался под своим именем. Но не каждый согласится со мной. Поэтому признаю, что Том привел в этом параграфе весьма полезные примеры переназначения клавиш. Увидев в приводимом ниже листинге символ AV, нажмите клавиши [CTRL-v], чтобы защитить следующую нажимаемую клавишу от интерпретации редактором ех (З1.м). В этом случае символ AV не будет запоминаться в макросе. При наличии двух таких символов ("V'-v) эта последовательность будет запоминаться в макросе как один настоящий символ [CTRL-v]. Файл на компакт-диске вполне пригоден для использования: в него записаны реальные управляющие команды. Попытайтесь воспользоваться его макросами! Еще одно замечание: если ваши макросы для файла .ехгс не работают, тщательно просмотрите сообщения об ошибках, выдаваемые редактором vi при запуске. Если не удается быстро их прочитать, воспользуйтесь приемами, о которых говорится в параграфе 42.08. — JP] 490 Часть пятая. Редактирование теиста
31.09 " МАКРОСЫ, РАБОТАЮЩИЕ В РЕЖИМЕ ВВОДА map! AZ A[:stopAM \xrc " Прервать сеанс редактирования. Заметьте, что установлена опция " autowrite. Поэтому команда тар! АА A[:stop!AM " прервет сеанс редактирования, не выполняя записи. it тар! ЛК AVA[0 " Эта команда позволяет в режиме ввода вставить строку над текущей. map! AVA[AB А[Ы " Вставка текста перед текущим словом. map! AVA[AF А[Еа " Вставка текста после текущего слова. и " МАКРОСЫ КЛАВИШ СО СТРЕЛКАМИ в стиле редактора EMACS II map! АВ AVA[i тар! AF AVA[lli тар! АА AVA[I тар! АЕ AVA[A II tl МАКРОСЫ ЗАМЕНЫ и тар v хр " В командном режиме поменять местами текущий и следующий символы. :т 33.04 тар V :т+1АМ " В командном режиме поменять местами текущую и следующую строки. map! AP AVA[hxpa " В режиме вставки поменять местами последний введенный и предпоследний символы. "т 30.04 гааР = " Редактировать ранее отредактированный файл. it ДРУГИЕ МАКРОСЫ map AW :wAM " Записать файл. и map * iAMA[ " Разбить строку. map AA :stop!/AM " Прервать сеанс редактирования. map Y у$ " Y является аналогом команд С и D. map AR ddu " Удалить и восстановить одну строку. map AN :n +/АМ " Перейти к следующему файлу из списка аргументов; такая же возможность " полезна для вызова команд типа vi +/string filel file2 file3. ti и " МЕТАМАКРОСЫ. Начинаются с метасимвола '\'; располагаются в файле после " других макросов. it map AVAI \ " В качестве метасимвола можно использовать как AI [tab — JP] , так и 'V. Создание пользовательских команд в редакторе vi 491
31.09 \<.\> 26.04 map \/ dePO/\<AVA [pA\>AVA ["wdd@w " Ищет в тексте текущее слово, использует буфер с именем w. к и " ИНВЕРТИРОВАНИЕ РЕГИСТРА СЛОВА "nDddn@n 3I.04 map \v ywmnoA[P:s/./WgAMO"nDdd'n@n п 30.34 « at,c -> две ABC -> abc map \V yWmnoA[P:s/./WgAMO"nDdd'n@n " abc.xyz -> ABC.XYZ ABC.XYZ -> abc.xyz и " МАКРОСЫ ВЫПОЛНЕНИЯ. Предназначены для выполнения существующих команд, и map \@ AVA["mdd@m " Выполняет строку как команду редактора vi (буфер.m, " для повторения операции использовать команду @@). :rt.tf.M map \! Oi:r!AVA["ndd@n " Выполняет команду :г! (буфер п, для повторения использовать команду @@). at 25.02 map \t :r!cat /dev/tty/AM " Прочитать содержимое буфера обмена X Window [в оконной системе. — JP]. и " БЛОЧНЫЕ МАКРОСЫ. Полезны, когда приходится работать с блоками кода на языке С. it map! А0 AVAVAV(AMAVAVAV) AVA[0AT " Этот макрос откроет блок (в фигурных скобках), не выходя из режима вставки. map! A] AVA[/AVAVAV)AVAMa " А этот макрос переместит вас за конец блока, не выходя из режима вставки. " LINT-МАКРОС. Удаляет весь текст, начиная от фразы lint output. " Вьшодимые программой lint данные располагаются на месте " удаленного текста в форме аккуратного блока комментария. Эту работу " может делать и редактор sed, но в данном случае получается быстрее. и " Следующая команда предназначена для использования в других переназначениях " и не должна применяться пользователями, хотя команда /ALo может иметь смысл. м map! A£o lint output и " А дальше то, что касается реальной работы. map \1 GoAM/* ALoAMA [/ALoAMdG:wAMo/*** ALoA [« :r!lint -u -lc %AV|sed 's/7 * /' AMGo***/A [N " [ПРИМЕЧАНИЕ: Мы поместили приведенный выше макрос в две строки'для " удобства при печати. В своем файле наберите его как одну строку. ~ JP] ■I " Мне нравится такой отступ. :%! 30.22 тар \1 :%! indent -i4AM и " КОММЕНТИРУЮЩИЕ МАКРОСЫ. Это просто изумительные помощники! it " Этот макрос в командном режиме заключает в символы комментария текущую " строку [для языка С. — JP] . map AX Ai/* A[A */А[А " А этот макрос снимает комментарии. map AY :s/\/\* \([A*]*\) \*V/\1A[ 492 Часть пятая. Редактирование текста
31.09 " Этот макрос отменяет негативные последствия рекурсии в конце файла, " вызванные в редакторе vi двумя следующими за ним макросами. тар! AN AVA[:unmap! AVAVAMA[ " В режиме вставки этот макрос поместит вас "внутрь" комментария. тар! -X AVA[:map! AVAVAM AVAVA [aAVAVAVANoA [a /* VA [hhi " В командном режиме макрос открывает блок комментария (AN для выхода) тар! \с 0/*ЛМ * AM*/A[k:map! AVAVAM AVAVAM* ЛМА " А этот макрос предназначен для добавления строк в блок комментария " (AN для выхода) . map! \o :map! AVAVAM AVAVAM* AMA less 25.04 тар _ i_AVAVAVAHAVA[ll " Текущий символ будет подчеркнут при выводе текста командами less, rn и др. МАКРОСЫ ДЛЯ ЗАПУСКА УТИЛИТЫ SPELL (проверка правописания) К spe]\29.01 map \s :wAMGoAVA[:$r! spell %AM " Выполнить проверку правописания в файле, слова с ошибками помешаются " в нижней части экрана; для поиска следующей ошибки используйте \п. map \n Gdd\/ " Предназначен для поиска следующей ошибки правописания. МАКРОСЫ ФОРМАТИРОВАНИЯ ftnt ЗОЛ expand 41.04 sed 3424 тар ii тар тар ii тар и тар \Р :.,$!fmt -75AM Форматировать документ до конца. \р !Jfmt -75AM Форматировать параграф. \f 1G/—AMj: .,$!fmt -75AM Форматировать сообщение (предполагается, что текст представлен в формате программы МН Mail). \е :%!expand -4AM Преобразовать символ табуляции в четыре пробела. \r 1G/A-/A[:r!sed -e 'l,/A$/d' -e 's/A./> &/' @ A[/A-/A[j " Прочитать сообщение и эаюпочить в кавычки (для ответов по электронной почте, " когда текущий файл обозначается как @) . map \d :s/$/ $/AM$r 74AV|? AVAMsAMA[$xxO " Разбить строку на строки стандартной длины (работает подобно утилите !!fmt). ab ЗОЛ ab Jan ab jan ab Feb ab feb ab Sep ab sep ab Oct ab oct ab Nov ab nov ab Dec ab dec ab Xmas ab xmas ab Mon ab mon January January February february September September October October November november December december Christmas Christmas Monday monday Созцание попьзоватепьских команд в редакторе vi 493
31.10 ab Tue Tuesday ab tue tuesday ab Wed Wednesday ab wed Wednesday ab Thu Thursday ab thu thursday ab Fri Friday ab fri friday ab Sat Saturday ab Sun Sunday ab Int International ab info information [Дополнительно приведем макрос Грега Уббена (Greg Ubpen), с помощью которого текущая строка центрируется: тар ЛК 801 ЛУЛ[$78hdO"D:s/ / /gAVAM$p (Символы Лк, Av, л [ и лм являются управляющими, но AD — это два отдельных символа.) Данный макрос применяется для центрирования текста в 80-символьной строке. Если строки по длине приближаются к 80-символьным, то макрос будет работать лучше при условии, что значение опции wrapmargin задано равным 0. — JP] - ТС 31.10 Переназначение повторяющихся нажатий клавиш Команда тар (31.02) редактора vi (а вернее, редактора ех) позволяет создавать пользовательские команды редактирования. Например, приведенная далее команда тар переопределяет клавишу [-] и назначает ей команды редактора vi: о (вставить новую строку под текущей), ESCAPE (перейти в командный режим), 75а- (добавить 75 знаков тире) и снова ESCAPE. :map - о"[75а-А[ Поэтому нажатие клавиши [-] приведет к заполнению строки, следующей за текущей, знаками тире. Проблема здесь в том, что в некоторых версиях редактора vi не разрешается задавать количество повторений, т.е. нельзя набрать команду 10-, чтобы добавить 10 строк, заполненных знаками тире. Выход состоит в том, чтобы определить другой макрос, который вызывает первый десять раз. Например, чтобы заполнить по нажатию клавиши [v] десять строк знаками тире, нужно создать такой макрос: :map v (Бред, не так ли? Но работает!) Переназначение для клавиши [-] можно поместить в файл .ехгс, а "мультипереназначения", подобные используемым при определении команды v, можно задавать во время работы редактора. - JP 31.11 Набор текста прописными буквами без переключения клавиши [CAPS LOCK] [Иногда весь текст, например программу на языке FORTRAN, необходимо набрать прописными буквами. Использование клавиши [CAPS LOCK] — занятие не из приятных, поскольку эту клавишу приходится нажимать почти каждый раз, когда необходимо набирать команду редактора vi. Ниже предлагается прекрасный метод набора текста строчными буквами и рассказано, как автоматически преобразовать их в прописные. — JP] Попытайтесь поместить следующие команды в файл .ехгс (зо.му. map! a A тар! b В тар! с С тар! z Z 494 Часть пятая. Редактирование текста
31.12 В режиме ввода текста редактор преобразует вводимую букву а в букву А. Что вы скажете об этом? Вы не заинтересованы в том, чтобы иметь такую возможность постоянно? В таком случае поместите команды переназначения в файл с именем './ (для программ на языке FORTRAN) и воспользуйтесь командой :source .£ когда появится необходимость ввести программу на указанном языке. Конечно, для выполнения этой команды можно определить функциональную клавишу (зг.оу. [Если необходимо задействовать строчные буквы, то, чтобы временно отменить переназначение, нажмите клавиши [CTRL-v]. Например, вводу команды :w должно предшествовать нажатие последовательности клавиш [CTRL-v] [w]. Вы можете войти в режим команд редактора ех, набрав команду Q редактора W. После выдачи приглашения : редактора ех макрос тар! перестает влиять на ввод данных. Чтобы вернуться из командного режима редактора ех в режим редактора и, наберите команду : vi. — JP\ — ВВ, из телеконференции net.unix в Usenet, 9 октября 1986 г. 31.12 Перемещение курсора в режиме ввода текста без использования клавиш со стрелками Некоторые пользователи не любят нажимать клавишу [ESC], когда во время работы с редактором v/ возникает необходимость переместить курсор. Комбинации [CTRL-h], [CTRL-j], [CTRL-k] и [CTRL-1] после переназначения можно будет использовать в режиме ввода текста для выполнения тех же действий, которые производят команды h, j, k и 1 в командном режиме. Примечание: В вашей системе сочетание клавиш fCTRL-h] назначено символу удаления (sm> вне редактора vz? Если да, то переназначение для [CTRL-h] (на клавиатуре обычно обозначается как [BACKSPACE]) изменит то действие, которое вызывает код [CTRL-h] в режиме ввода текста: вместо удаления символов, набираемых пользователем, будет происходить перемещение курсора назад по введенному тексту без его удаления. Один из способов избежать этого — связать символ стирания с клавишей [DELETE] или [RUBOUT], введя перед запуском редактора W команду stty erase '"?'. В таком случае нажатие клавиши [DELETE] будет приводить к стиранию того, что вводится, а клавиша [BACKSPACE] будет вызывать "перепрыгивание назад" через введенные символы, без их стирания. Вот команды, которые следует поместить в файл .ехгс (4.09): тар! ЛН A[i тар! АК л[ка тар! ЛЬ л[1а тар! "V -[ja " Примечание: в двух последних строках переназначается "J (символ перевода строки) Последнее переназначение занимает две строки. Такой мудреный прием "пройдет" не во всех версиях редактора W. Нет, это не переназначение для сочетания клавиш [CTRL-v], хотя и похоже на него. Это — переназначение для символа AJ (клавиша [LINEFEED] — "перевод строки"). Символ Av располагается в самом конце строки переназначения. При вводе этого переназначения нажмите клавиши [CTRL-v], а затем — [LINEFEED] или [CTRL-j]. После перемещения курсора на начало следующей строки нажмите клавишу [ПРОБЕЛ], а затем введите остальной текст макроса. Кстати, еще одна хорошая мысль: добавьте комментарий с напоминанием о цели переназначения в строку, следующую после строки с переназначением (двойная кавычка вначале является признаком комментария). Создание пользовательских команд в редакторе vi 495
из Примечание: Переназначение для сочетания клавиш [CTRL-j] достаточно наглядно выявляет то, о чем авторы версии редактора v/, которой я пользуюсь, не задумывались. Взгляните, например, на ту "мешанину", которая появляется при попытке вывести перечень своих переназначений клавиш для режима ввода текста: :тар! ЛН АН л[1 АК АК А[ка ЛЬ AL А[1а Alja При работе с файлом, к потере которого вы будете не равнодушны, данное переопределение, прежде чем его использовать, наверное, следует тщательно протестировать. - JP 31.13 Управление курсором в режиме ввода текста (9] exit В этом параграфе я привожу свой файл .ехгс, переделанный для терминала VTI00. Содержащиеся в нем макросы удобны для использования при программировании на С и с командой nroff -me. [Строки наподобие Л [OD порождаются клавишами управления курсордм (41.12) на терминалах типа VT100. Символы А [ вставляются нажатием клавиш [CTRL-v][ESC]. Многие версии редактора vi выполняют эти действия более простым способом, так как "понимают", что команда тар #1 (З1.ю) означает "переназначить функциональную клавишу [F1]". — Щ set ai redraw sm wm=l set tabstop=4 set shiftwidth=4 map! (} (АМ}Л[0А1 Великолепный макрос для автоматического сдвига пары скобок {} map! А[OD A[ha Переместить курсор влево во время нажатия клавиши со стрелкой, указывающей влево Переместить курсор вверх во время нажатия клавиши со стрелкой, указывающей вверх Переместить курсор вниз во время нажатия клавиши со стрелкой, указывающей вниз Переместить курсор вправо во время нажатия клавиши со стрелкой, указывающей вправо Использовать клавишу F1 для добавления текста в. конец строки Га же функция назначается клавише F1 в режиме ввода текста Использовать клавишу F2 для вставки текста в начало строки Га же функция назначается клавише F2 в режиме ввода текста Левая кавычка для утилиты troff Правая кавычка для утилиты troff Я могу использовать клавиши перемещения курсора во время ввода текста. Это удобно, поскольку можно вернуться назад и добавить текст, не нажимая клавиши [ESC]. [Правда, при этом может быть утеряна возможность восстановления (зюз) предыдущих изменений с помощью команды u. — JP\ Я также переопределил клавишу [F1], в результате чего могу добавлять текст в конец строки. И теперь, независимо от того, в каком режиме я нахожусь, клавиша [F1] всегда переводит меня в режим ввода текста. Однажды я даже переназначил все клавиши дополнительной цифровой клавиатуры, чтобы эмулировать редактор EDT. — ВВ, из телеконференции net.unix в Usenet, 9 октября 1986 г. 496 Часть пятая. Редактирование текста ШГ 43.13 тар! тар! тар! тар тар! тар тар! тар! тар! А[ОА А[ОВ "[ОС А[ОР А [ОР A[OQ A[OQ * * 1 1 A[ka A[ja A[la A A[A Oi A[0i \*dq \*(rq
31.15 31.14 Не теряйте важных функций из-за переназначений клавиш — пользуйтесь опцией погетар На протяжении ряда лет я предполагал, что могу переназначать (ЗЮ2) только несколько символов в редакторе vi, например v и *А, которые нигде не используются. Причем, переназначая для выполнения каких-либо действий, скажем, символы AF, я считал, что теряю удобную команду "переместиться на одну экранную страницу вперед". Вы тоже так думали? Тогда мы ошибались с вами вместе! Рассмотрим опцию погетар, пример использования которой приведен ниже. В данном случае создается команда AF — "отобразить информацию о файле" (обычно для этой цели применяется команда AG), а затем создается команда АА, которая берет на себя функцию "переместиться на одну экранную страницу вперед". Поместите переназначения этих клавиш в свой файл .ехгс (зому. set noremap тар AF AG тар АА AF - JP 31.15 Как обойти ограничения редактора vi и заставить его выполнять сложные макросы Редактор w предпочитает макросам команду undo. К сожалению, выполняемая при этом операция отмены последнего действия имеет ограниченную область применения. К тому же, вместо того чтобы позволить сложным макросам отключать возможность выполнять команду undo, редактор и запрещает использовать сами сложные макросы. Считаю, что это большой просчет. Я, скорее бы, отказался от команды undo, чем от макросов. К счастью тех, кто хочет работать с настоящими макросами, подпрограмму, которая определяет, собирается ли макрос приостановить действие команды undo, можно обмануть. Так, включение команды шх (обозначить местоположение маркером х) в начале макроопределения часто "умиротворяет" редактор vi. Почему-то это заставляет его работать по-другому, и проверка в этом случае не является столь строгой. Обойти ограничения редактора vi позволяет и разделение одного макроса на несколько вызывающих друг друга макросов. Например, для выполнения одного сложного макроса я задаю переназначение тар X "ЬУ а затем выполняю команду lGkwEX@b вместо команды lGkwE"bY@b Непосредственное применение команды "ЬУ ничего не дает, но использование макроса X приводит (по крайней мере, в большинстве систем) к положительному результату. Что же тогда означает получаемое сообщение can't put inside a global/macro? Возможно, поставщик UNIX "исправил" редактор vi, с тем чтобы тот распознавал ситуацию, когда команда yank (копировать) может приостанавливать команду undo, даже если последняя вызывается из макроса. Не миритесь с такой ситуацией! Объясните поставщику своей системы UNIX, что вы не являетесь пользователем MS-DOS и устали от подобной опеки. Не позволяйте вечно во все вмешивающимся поставщикам UNIX-систем разрушать старые добрые пользовательские традиции, которые придают UNIX такое величие. Потребуйте от них, чтобы все оставалось прежним. -DH Создание пользовательских команд в редакторе vi 497
30.38 30.38 Как найти предыдущее место редактирования с помощью команды undo Часто во время редактирования файла приходится переходить в другую его часть, с тем чтобы возобновить в памяти отдельные детали, отменить или внести какую-то правку. Как же в таком случае можно вернуться в предыдущее место? Это место можно пометить с помощью команды т. В командном режиме введите ш, после чего укажите любую букву. (Мы будем использовать в примере букву х.) Ниже приводятся команды, которые выполняют требуемые действия. тх Помечает текущую позицию маркером х. 'х Перемещает курсор на первый символ строки, помеченной маркером х. 'х Перемещает курсор на символ, помеченный маркером х. Перемещает курсор в позицию, где последний раз выполнялось редактирование или был поставлен маркер. '' Перемещает курсор в начало строки, где последний раз выполнялось редактирование или был поставлен маркер. Но часто для отмены последнего действия редактора бывает достаточным ввести команду и. Данная команда возвращает пользователя именно в то место, где он перед этим работал, и отменяет сделанные исправления. Чтобы восстановить результат редактирования, необходимо опять ввести команду и. (Я по-прежнему использую команду ш, если необходимо отметить сразу несколько правок.) - TOR
32 GNU Emacs 32.01 Emacs — еще один редактор Еше одним редактором, получившим всеобщее признание, является Emacs. Его версии имеются почти для всех доступных систем, однако самая удачная и наиболее широко используемая среди них — это GNU Emacs, разработанная организацией Free Software Foundation (FSF). ^^^_ Редактор GNU Emacs считается самым мощным в семействе Emacs. Распространяется он ш ш 1 бесплатно, на условиях Всеобшей Открытой Лицензии (General Public License) организации ^^_J FSF. (GNU Emacs имеется на прилагаемом компакт-диске.) Несомненно, между редактором ^щю Emacs, предоставляющим мощную и богатую рабочую среду, и редактором у/ существует такая же разница, как, скажем, между различными религиями. Чем еще примечателен редактор Emacs кроме того, что он распространяется бесплатно? Индивидуальных особенностей, которые можно было бы перечислить, у него несметное количество (см. параграф 32.02). Но самая замечательная среди них, на мой взгляд, — это способность работать в тесном взаимодействии с UNIX. В частности, имеется встроенная система электронной почты р.зз), благодаря чему пользователь может посылать и принимать почту, не выходя из Emacs. Редактор также включает средства для редактирования файлов (их удаления, копирования, переименования), запуска интерпретатора shell и т.д. Правда, интерпретатор С shell имеет несколько неудобный механизм управления перечнем введенных команд; в интерпретаторе Когп shell этот механизм реализован чуть лучше. А представьте себе, что вы можете так же легко вызывать и редактировать команды, как и писать письма! Это намного превосходит возможности любого интерпретатора shell, но выглядит довольно просто, поскольку вы запускаете shell из своего редактора. Мы не можем уделить Emacs столько же внимания, как редактору v/ fjo.oij, но хотим отметить его самые интересные возможности и рассказать о нескольких приемах, которые позволят вам использовать этот редактор с максимальной эффективностью. — ML, BR, DC, из книги Learning GNU Emacs издательства O'Reilly & Associates 32.02 Особенности редактора Emacs Привожу обещанный перечень тех особенностей редактора Emacs, которым я отдаю предпочтение. Окна Emacs — это "оконный" редактор. Еще не было в помине системы X Window и компьютеров Macintosh, a Emacs уже делил экран терминала на несколько "окон", позволяя в каждом из них выполнять различные действия. Пользователь мог в окнах редактировать разные файлы или в одном окне читать почту, в другом давать ответы на полученные письма, в третьем выполнять команды интерпретатора shell и т.д. GNU Emacs 499
12.02 Командные интерпретаторы Макросы Режимы редактирования Почта Сейчас, когда мы все имеем рабочие станции, оснащенные мышью и другими прекрасными инструментами для перемещения по графическому экрану, о чем еще можно мечтать? Ну, во-первых, не у всех имеется графический монитор (даже если он есть у вас дома, на работе его может не быть). Во-вторых, я до сих пор отдаю предпочтение редактору Emacs, а не "современным" оконным системам, поскольку мышью обычно не пользуюсь. Если необходимо создать еще одно окно, я просто нажимаю клавиши [CTRL-x][2] (что приводит к разделению текущего окна, каким бы оно ни было, на два новых); при желании работать в другом окне я просто нажимаю [CTRL-x], а для того чтобы удалить окно, нажимаю [CTRL-x][0]. Быстрее ли это, чем тянуться за мышью, чтобы выполнить с ее помощью какие-либо манипуляции? Несомненно! Особенно, если мышь в это время завалена кучей бумаг. (Конечно, она завалена — ведь я так редко в ней нуждаюсь!) Создав окно, легко приступить к редактированию нового файла, инициировать сеанс работы с интерпретатором shell и т.п. Запустить интерпретатор shell в интерактивном режиме можно в любом окне редактора Emacs. Нажмите клавишу [ESC], введите команду х shell, и вы увидите знакомое приглашение командной строки. Легко догадаться, в чем здесь преимущество: вы можете быстро возвратиться к предыдущим командам, скопировать, а затем отредактировать их. Еще более важной является следующая возможность: в редакторе Emacs нетрудно получить вывод команды и скопировать его в текстовый файл. Совершенно очевидно, насколько это удобно при написании, скажем, книги, подобной этой. Редактор также позволяет задавать команды, которые выполняют действия над содержимым окна или какой-либо выбранной его части. Редактор Emacs позволяет определять макросы — последовательности команд, которые могут выполняться автоматически. Это напоминает переназначение клавиш (зш) в редакторе w. Правда, Emacs выполняет команды, когда пользователь определяет макрос, в то время как vi предполагает, что вы сначала сформулируете идею, а затем введете необходимые команды, не имея с редактором обратной связи, в надежде, что макрос не выполнит каких-либо нежелательных действий. С помощью редактора Emacs легче добиться, чтобы макрос работал правильно, поскольку вы видите, что будет делать макрос, еще во время его определения, и если допускаете ошибку, можете немедленно ее исправить. Редактор Emacs имеет большое число специальных режимов редактирования, которые предоставляют "контекстную" помощь при наборе текста. Например, во время ввода программы на языке С установка соответствующего режима работы поможет вам соблюдать соглашения, касающиеся отступов и комментариев в программе. В этом. режиме редактор автоматически ставит на место фигурные скобки и подсказывает, где и какой скобки не хватает. Специальные режимы существуют, наверное, для каждого языка программирования, о котором я когда-либо слышал. Есть специальные режимы для работы с HTML, troff, ТЕХ, для создания блок-схем и наклеек. Хотя я часто рассказываю о такой особенности редактора Emacs, как работа с почтой, последнюю я практически не использую. Тем не менее, если вам по-настоящему нравится работать в среде редактора Emacs, попробуйте использовать и эту возможность. 500 Часть пятая. Редактирование текста
32.04 Настройка Среди всех редакторов, с которыми мне когда-либо приходилось сталкиваться, наиболее адаптируемым к потребностям пользователя инструментом является Emacs. В основе его гибкости лежит язык программирования LISP, поэтому вы, решив научиться настраивать редактор Emacs, должны познакомиться и с данным языком. Это позволит вам делать практически все. Например, я не сомневаюсь, что в редакторе Emacs можно написать полную программу работы с электронными таблицами, а это значит, что можно будет использовать обычные команды редактора для редактирования электронных таблиц и их вставки в свои документы. (Не исключено, что электронные таблицы, использующие возможности редактора Emacs, уже существуют, но мне об этом не известно.) В соответствии с условиями Всеобщей Открытой Лицензии организации FSF (si.oi) все специальные пакеты распространяются бесплатно. - ML 32.03 Параметры и способы их изменения Обычно настройки редактора Emacs хранятся в файле с именем .emacs, который расположен в начальном каталоге пользователя. В параграфе 32.07 приводятся примеры нескольких установок, которые я лично нахожу достаточно удобными. Если вы похожи на большинство других пользователей, которых я знаю, то рано или поздно добавите себе эти установки, даже если не программируете на языке LISP. И, будучи знакомыми с другими пользователями Emacs, вы обязательно начнете заимствовать что-то у них. Лучший способ настроить редактор "на свой вкус" — посмотреть, как сделано у других. Кстати, и я тоже большую часть настроек для своего файла позаимствовал у других пользователей. Однако следует также знать и о "темной стороне" такой свободы действий. Что произойдет, если вы сядете за чей-либо терминал, запустите редактор Emacs и обнаружите, что благодаря своей настройке он стал неузнаваемым? Или этот "всегда готовый помочь" администратор "наинсталлировал" таких общесистемных утилит, которые постоянно попадаются вам на глаза, сбивая с толку? Вот что может помочь в таком случае: задайте команду emacs с опцией -д. Она послужит редактору Emacs указанием не загружать никаких файлов инициализации .emacs. (Если хотите загрузить собственный файл инициализации, попробуйте задать опцию -и имя_фаша.) Но это еще не решит проблем общесистемных установок. Чтобы они вам не мешали, поместите в начало своего файла .emacs следующую строку: (setq inhibit-default-init t) Данная команда отключит всю "глобальную" инициализацию. (При совместном использовании терминала с другим пользователем вам по-прежнему будет нужна опция -и: она заставит редактор Emacs читать ваш файл инициализации.) - ML, DC, BR 32.04 Резервные файлы и файлы автосохранения Очень часто пользователь, потратив на редактирование файла несколько часов, приходит к выводу, что первоначальная его версия была лучшей. Или нажимает какую-то странную последовательность клавиш, вследствие чего с редактором Emacs начинает твориться что-то непонятное, чего нельзя отменить даже командой undo. Редактор Emacs предоставляет несколько способов для выхода из подобных ситуаций. Во-первых, попытайтесь ввести команду [ESC-x] revert-buffer. Редактор Emacs задаст вам один из двух вопросов: либо Buffer has been auto-saved recently. Revert from auto-saved file? (y or n) (Для буфера недавно было выполнено автосохранение. GNU Emacs 501
32.0S Восстановить из файла автосохранения? (у — да, п — нет)), либо Revert buffer from file your-filename? (у or n) (Восстановить буфер из файла имя_файла! О — да, л — нет)). Но вы не сможете решить, что делать, если не поймете различия между этими вопросами. Редактор Emacs создает файл автосохранения* после каждых трехсот нажатий клавиш. Поэтому если вернуться к файлу автосохранения, то потеряется, самое большее, текст, введенный в результате трехсот последних нажатий клавиш. Возможно, этого будет достаточно, но не исключено, что ошибка произошла давным-давно. В таком случае использовать файл автосохранения нет смысла. Введите п в ответ на первый вопрос, и на экране появится второй вопрос, в котором спрашивается, не хотите ли вы вернуться к последней сохраненной копии файла. Введите yes, чтобы загрузить последнюю версию файла. Возможно, вам будет задан только последний вопрос (Revert buffer from file . . .). Это значит, что редактируемый файл был сохранен во время последних трехсот нажатий клавиш. После сохранения файла редактор Emacs удаляет файл автосохранения, который будет заново создаваться с указанной периодичностью. Следует заметить, что редактор Emacs очень разборчив к вводимой информации. Если он просит ввести у или п, то следует ввести именно у или п, а если yes или по, то именно yes или по. На подобные тонкости всегда нужно обращать внимание. Если же вам действительно необходимо вернуться к первоначальному варианту файла, рекомендуем восстановить резервный файл редактора Emacs. При внесении изменений в уже существующий файл редактор Emacs сразу после запуска создает резервный файл. Если редактируется новый файл, то Emacs создаст резервную копию этого файла после того, как пользователь во второй раз сохранит его. Вновь созданный резервный файл остается неприкосновенным, т.е. сохраняется на диске, до следующего запуска редактора Emacs. При запуске редактора создается новая резервная копия, в которой отображается содержимое файла, соответствующее началу сеанса редактирования. Теперь мы готовы рассмотреть вопрос о том, как происходит восстановление резервного файла. В редакторе Emacs для этой цели каких-либо особых команд не имеется. Все делается вручную. (Имя резервного файла совпадает с именем первоначального файла, правда, к нему добавляется ~ (тильда).) Поэтому выйдите из редактора (или запустите интерпретатор shell) и наберите команду % mv имя_файла~ имвг_файла Отметим, что редактор Emacs обладает способностью сохранять "пронумерованные" резервные файлы, как это делается в операционной системе VAX/VMS. Но мы никогда не использовали эту возможностью и не считаем ее хорошей идеей. Но такая возможность существует, и вы можете ею воспользоваться. - ML, DC 32.05 Перевод редактора Emacs в режим замены Многие пользователи привыкли работать с редакторами, которые обычно находятся в режиме замены:" вводимый текст "затирает" существующий. Редактор Emacs по умолчанию работает в режиме вставки: новые символы вставляются в позиции курсора. Если вы отдаете предпочтение режиму замены, введите команду [ESC-x] overwrite-mode. Воспользовавшись дополнением команд (32.06), это выражение можно привести к такому виду: [ESC-x] ov. На многих клавиатурах нажатие клавиши [INSERT] также переводит редактор в режим замены. Вы можете воспользоваться этой командой и для того, чтобы отключить указанный режим. * Имя файла автосохранения имеет следующий вид: #имя_файла#, т.е. в начало и конец "обычного" имени файла добавляется символ #. ** К таковым относятся некоторые редакторы для мейнфреймов, например XEDIT, и (насколько я помню) множество других средств для редактирования и обработки текстов. 502 Часть пятая. Редактирование текста
32.07 Но если вы хотите всегда работать в режиме замены, создайте в своем каталоге файл .emacs и поместите в него следующую команду: (setq-default overwrite-mode t) Подробнее о том, как настроить редактор Emacs, вы можете узнать из книги Learning GNU. Emacs, написанной Деброй Камерон (Debra Cameron) в соавторстве с Биллом Розенблаттом (Bill Rosenblatt) и выпушенной в издательстве O'Reilly & Associates. - ML, DC 32.06 Дополнение команд Редактор Emacs обладает таким интересным свойством, как дополнение команд. По существу, это означает, что редактор Emacs требует ввода минимального количества необходимой информации, а остальное дополняет сам. Данную возможность можно использовать, например, при вводе имени файла, названия буфера, имени команды или переменной. Просто введите несколько букв из имени, чтобы они его однозначно идентифицировали (обычно это первые буквы), а затем нажмите клавишу [TAB]. Остальную часть имени "допишет" редактор. Если имя не является уникальным, т.е. если есть другие файлы, которые начинаются с тех же букв, редактор Emacs предложит альтернативные варианты. Введите еще несколько букв, выбрав таким образом нужный файл, а затем снова нажмите [TAB]. Так, при необходимости загрузить файл outline.txtя просто задаю команду [CTRL-x] [CTRL-f ] out [TAB]. Если никаких других имен файлов, начинающихся с букв out, не существует, редактор Emacs дополнит имя. Убедившись в том, что все правильно, я нажимаю клавишу [RETURN], и команда выполняется. Таким образом, при использовании принципа дополнения команд необходимо убедиться, что редактор нашел нужный файл. Если этого не сделать, результаты могут быть самыми неожиданными (в частности, могут быть подставлены только часть имени или вообще не то имя). - ML, BR 32.07 Любимые методы для экономии времени Я набираю текст очень быстро, стараясь не пользоваться функциональными клавишами, клавишами со стрелками и даже мышью. Меня очень раздражает все, что отвлекает от основной алфавитно-цифровой клавиатуры. Даже необходимость использовать клавиши [BACKSPACE] и [DELETE] вызывает у меня недовольство, поскольку вынуждает менять положение руки. Исходя из этих соображений, я настроил редактор Emacs таким образом, что могу делать действительно все на основной алфавитно-цифровой клавиатуре, используя при необходимости клавишу [Ctrl]. Ниже приводятся некоторые фрагменты из моего файла .emacs. ;; Задать удаление предыдущего символа с помощью клавиш [CTRL-h]. ;; В обычных случаях это сочетание вызывает справочную систему. (define-key global-map "\C-h" 'backward-delete-char) ;; Гарантировать также работу клавиш [CTRL-h] при поиске. (setq search-delete-char (string-to-char "\C-h")) ;; Закрепить вызов справочной системы за другой клавишей ([CTRL-_]). ;; ПРИМЕЧАНИЕ: сочетание [CTRL-_] на некоторых терминалах не определено (define-key global-map "\C-_" 'help-command) ;; замещение (setq help-char (string-to-char "\C-_")) ;; Задать удаление предыдущего слова с помощью клавиш [ESC-h]. (define-key global-map "\M-h" 'backward-kill-word) ;; Задать выполнение команды undo (отменить) с помощью клавиш [CTRL-x][CTRL-u]. ;; Это лучше, чем [CTRL-x][и], поскольку не надо отпускать клавишу [CTRL]. (define-key global-map "\C-x\C-u" 'undo) ;; Прокрутить экран вверх или вниз на одну строку с помощью клавиш [CTRL-z] и [ESC-z]. GNU Emacs 503 .emacsml
\2.08 . (defun scroll-up-one () "Scroll up 1 line." (interactive) (scroll-up (prefix-numeric-value current-prefix-arg))) (defun scroll-down-one () "Scroll down 1 line." (interactive) (scroll-down (prefix-numeric-value current-prefix-arg))) (define-key global-map "\C-z" 'scroll-up-one) (define-key global-map "\M-z" 'scroll-down-one) ;; Использовать [CTRL-x] [CTRL-v] , чтобы "заглянуть" в новый файл, оставляя ;; предыдущий файл на экране. (define-key global-map "\C-x\C-v" 'find-file-other-window) Комментарии (строки, начинающиеся двумя символами ;) предназначены для объяснения того, что делают команды. Определитесь, что вам требуется, и добавьте соответствующие команды в свой файл .emacs. Наиболее важные команды приводятся в начале файла. - ML Рациональный поиск Редафор jjmacs предоставляет в наше распоряжение порядка сотни различных команд поиска! (Ладно, на самом деле их чуть более тридцати, но кто будет считать?) Имеются команды поиска на все вкусы: для инкреМентного поиска, поиска слов,* поиска регулярных выражений и т.п. Однако когда дело доходит до самого обыкновенного поиска, редактор Emacs становится, как это ни странно, не таким уж и совершенным. Существует команда простого поиска, которая способна искать произвольную последовательность символов, но найти эту команду довольно сложно. К тому же ей не хватает одного очень важного свойства; не имеется возможности осуществлять повторный поиск строки, т.е. вы не можете сказать: "Хорошо, одна правильная последовательность символов найдена. Давай поищем следующую". Вам придется каждый раз по-новому набирать текст той строки, которую необходимо найти. Я считал это напрасной тратой времени, пока мой друг не показал мне специальную команду поиска- Она содержится в файле search.el. Поместите его в свой каталог для утилит редактора Emacs (4M) и добавьте в файл .emacs примерно следующее: -;; Команды поиска, любезно предоставленные Крисом Дженли. -' ' >; Подставьте свой каталог для утилит Emacs вместо /home/los/mikel/emacs (load-file "/home/los/mikel/emacs/search.el") Теперь можно будет воспользоваться клавишами [CTRL-s] при необходимости выполнить поиск в направлении вперед, к концу файла, и клавишами [CTRL-r] — в обратном направлении. Редактор Emacs попросит вас ввести строку для поиска и начнет таковой после нажатия клавиши [RETURN]. В случае повторного нажатия клавиш [CTRL-s] или [CTRL-r] поиск уже введенной строки выполняется еще раз. Здесь обнаруживается еще одно полезное свойство: в отличие от других команд поиска редактора Emacs данные команды выводят на экран строку поиска, заданную по умолчанию (т.е. последнюю строку, которая находится в буфере). Это именно тот вид поиска, который меня интересовал. Не исключено, что рано или поздно вам захочется применить команды инкрементного поиска. Только сначала эти команды придется переназначить, чтобы ими было удобно пользоваться. Приведу варианты переназначения клавиш, которыми я пользуюсь. ;;. Связать инкрементный поиск с клавишами [ESC-s] и [ESC-r] (define-key global-map "\M-s" 'isearch-forward) (define-key global-map "\M-r" 'isearch-backward) ;; Необходимо отдельно переназначить клавиши [ESC-s] для перехода ;; в текстовый режим. ;; Обычно эта комбинация клавиш связана с командой "центрировать строку" (define-key text-mode-map "\M-s" 'isearch-forward) • Это особенно приятно, поскольку позволяет производить поиск фраз, которые переходят на другую строку. Но если используются программы troffwm TEXw.w. редактор Emacs может быть "сбит с толку" разметкой текста. 504 Часть пятая, Редактирование текста 32.08 В uarch.eS
32.10 Сочетания клавиш [ESC-s] и [ESC-r] теперь представляют собой команды йнкрементного поиска впереди и в обратном направлении. Инициировав однажды инкрементный поиск, его можно будет повторить, воспользовавшись комбинациями клавиш [CTRL-s] и [CTRL-r], как и было задумано. Конечно, теперь вам придется переназначить команду "центрировать строку", если вы любите ею пользоваться. Но, по-моему, она не стоит того, чтобы о ней заботились. Игра под названием "Переназначение клавиш" на этом прекращается. - ML 32.09 Сброс переменной среды PWD перец использованием редактора Emacs Я сталкивался с рядом странных ситуаций, когда редактор Emacs не мог найти файл, пока не было задано его полное путевое имя (i.2i). При попытке обычного доступа к файлу редактор выдавал сообщение File not found and directory does not exist (Файл не найден, поскольку указанный каталог не существует). Обычно это означает, что переменная среды PWD (ь.оз) интерпретатора С shell установлена неправильно. Существует несколько способов обманным путем заставить интерпретатор С shell сделать эту ошибку. Я также видел несколько систем, в которых интерпретатор С shell вставлял в переменную PWD лишние символы косой черты, т.е. значение этой переменной выглядело как /home/mike//Mail, а не как /home/mike/'Mail. Системе UNIX это безразлично, она не считает ввод лишних символов косой черты ошибкой. Но редактор Emacs интерпретирует символы // как имя корневого каталога, т.е. отбрасывает все, что находится слева от них. Поэтому при попытке отредактировать файл /home/mike//Mail/output.txt редактор Emacs будет искать файл /Mail/output.txt. Даже если таковой существует, это не то, что вам нужно. [То же самое происходит, когда редактор Emacs вызывается из сценария интерпретатора Bourne shell, в котором изменяется текущий каталог, но остается неизменной переменная PWD. — JP] Эта проблема вызывает особенную досаду, так как интерпретатор shell автоматически переустанавливает переменную PWD каждый раз при смене каталога. Поэтому наиболее очевидное решение — вставить в свой файл .emacs команду unsetenv PWD — не приведет ни к чему хорошему. В данном случае следует определить псевдоним ao.oi) для следующей команды: (..) 13.07 alias gmacs "(unsetenv PWD; emacs \!*)" Наилучшим решением может быть использование другого интерпретатора shell, в котором такая проблема не возникает. Интерпретатор Bourne shell (sfi), очевидно, для этой цели не годится, поскольку он не отслеживает текущий каталог. - ML 32.10 Вставка в файлы управляющих символов Я помню, как меня доводил чуть ли не до умопомешательства один парень (надеюсь, он не прочтет того, что я пишу), который очень часто спрашивал, как вставить символ разрыва страницы в текстовый файл. Он печатал на принтере, для которого вставка этого символа была привычным делом: необходимо было всего лишь ввести команду CTRL-1. Но эта команда уже означала для редактора Emacs команду обновить экран. Как же вставить символ разрыва страницы в файл, запретив редактору Emacs воспринимать его в качестве команды обновления? Очень просто: путем ввода перед CTRL-1 команды CTRL-q. Последняя указывает редактору Emacs, что следующий вводимый символ не является частью команды. Поэтому сочетание клавиш [CTRL-q] [CTRL-1] вызовет вставку в файл кода, соответствующего сочетанию клавиш [CTRL-lj; на экране при этом отобразится Ль. (Обратите внимание, что такая комбинация подразумевает один символ, а не два.) Данный символ, в свою очередь, при печати на принтере в нужном месте будет вызывать прогон отпечатанного листа бумаги. GNU Emacs 505
32.11 Этим методом можно воспользоваться для вставки в файл с помощью редактора Emacs любого управляющего символа. Я сам применяю данный метод в особо затруднительных ситуациях. В то же время хочу сказать, что злоупотреблять им не стоит. Просто вы должны знать, что есть такая возможность. - ML 32.11 Использование режима автозамены Подобно редактору v/, редактор Emacs предоставляет такое полезное средство, как автозамена. Имеется в виду возможность определять сокращения для длинных слов и фраз. Предположим, вы набираете текст договора, в котором часто используется название National Institute of Standards and Technology. Вместо того чтобы полностью набирать это название, вы можете определить сокращение nist. Редактор Emacs будет вставлять полное название данного учреждения каждый раз при вводе nist с последующим пробелом или знаком препинания. Редактор Emacs отслеживает, не вводится ли сокращение, и автоматически производит замену, как только нажимается клавиша [ПРОБЕЛ] или вводится знак препинания (например, ., ,, !, •р . -ч -1 /1 • }■ Одним из применений режима автозамены является исправление орфографических ошибок при вводе текста. Почти у каждого человека есть дюжина (или около того) слов, которые он по привычке набирает с ошибками. Редактору Emacs можно просто указать, что эти ошибки являются сокращениями (заменой) правильных слов, и он будет автоматически исправлять их. Если уделить немного времени и задать сокращения для наиболее часто встречающихся опечаток, то программа проверки правописания не станет надоедать вам такими словами, как teh, adn и т.п. Редактор Emacs поработает с опечатками без вашего ведома и сам исправит их. Скажем, вы определили слою teh в качестве сокращения (замены) для слова the. Тогда при нажатии клавиши [ПРОБЕЛ] после ввода слова teh редактор Emacs сразу же исправит его. Возможно, вы даже не заметите, что была допущена ошибка. Сокращения слов для одного сеанса работы с редактором Обычно сокращение слова используется на протяжении нескольких сеансов работы с редактором Emacs. Если же вы, скажем, просто хотите поэкспериментировать с режимом автозамены, чтобы посмотреть, стоит ли устанавливать его при запуске редактора, воспользуйтесь приведенной ниже процедурой. Чтобы определить автоматически заменяемые слова для одного сеанса работы, выполните следующие действия. 1. Перейдите в режим сокращения слов с помощью команды [ESC-x] abbrev-mode. В строке режима появится надпись abbrev. 2. Введите сокращение, которое хотите использовать, и нажмите клавиши [CTRL-x][a][-]. Редактор Emacs попросит расшифровать сокращение. 3. Введите текст расшифровки и нажмите клавишу [RETURN]. Редактор Emacs заменит сокращение нужным словом или выражением и будет делать это каждый раз, когда после данного сокращения будет вводиться пробел или знак препинания. Заданное сокращение будет действовать на протяжении лишь одного сеанса работы с редактором Emacs. Если вам нравится принцип работы с сокращенными словами, задайте режим автозамены при запуске редактора, о чем пойдет речь в следующем параграфе. Установление режима автозамены во время запуска редактора Если вы хотите использовать режим автозамены, задайте в файле .emacs возможность его установки, и вы сможете автоматически переходить в данный режим и загружать свой файл сокращений. Чтобы определить сокращения и сделать их доступными при запуске редактора, выполните следующие действия. 1. Поместите в свой файл .emacs такие строки: (setq-default abbrev-mode t) (read-abbrev-file "~/.abbrev-defs") (setq save-abbrevs t) 506 Часть пятая. Редактирование текста
32.12 2. Сохраните файл .emacs и перезапустите редактор Emacs. В строке режима вы увидите надпись Abbrev. (В этот момент может появиться сообщение об ошибке; не обращайте на него внимания, оно больше не повторится.) 3. Введите сокращение, которое хотите использовать, а после него команду [CTRL-x] a -. Редактор Emacs попросит расшифровать сокращение. 4. После ввода расшифровки аббревиатуры нажмите клавишу [RETURN]. Редактор Emacs заменит сокращение целым словом или выражением и будет повторять эту операцию каждый раз, когда после сокращения будет вводиться пробел или знак препинания. Повторно выполняя шаги 3 и 4, можно определить столько сокращений, сколько необходимо. 5. Введите команду [ESC x] write-abbrev-file, чтобы сохранить файл сокращений. Редактор Emacs попросит ввести имя файла. 6. Введите ~/.abbrev-defs, после чего редактор Emacs создаст новый файл. Данный шаг выполняется только при первом определении сокращений в соответствии с описанной процедурой. Если этот файл существует, то команды, содержащиеся в файле .emacs, вызовут автоматическую загрузку файла сокращений. После того как данная процедура будет один раз выполнена, для определения дополнительных сокращений необходимо повторять только шаги 3 и 4. При вводе сокращений слов в последующих сеансах работы редактор Emacs спрашивает, не нужно ли сохранить файл сокращений. Введите у, чтобы сохранить вновь введенные сокращения и позволить редактору автоматически активизировать их. Для удаления сокращений, которые больше не нужны, предназначена команда edit-word- abbrevs. - DC 32.12 Решение проблемы управления потоками Пользователям редактора Emacs нередко приходится сталкиваться с проблемой управления потоками (flow control) (41.02), возникающей при использовании удаленных терминалов. (На рабочих станциях и персональных компьютерах подобная проблема не возникает). В отдельных операционных системах и устройствах передачи данных (терминалах, модемах и т.д.) символы CTRL-s и CTRL-q используются как знаки останова и продолжения терминального ввода-вывода, предотвращающие переполнение буфера. Во многих современных системах применяются другие средства управления потоками, и в частности дополнительное аппаратное оборудование, но иногда используется и старое соглашение относительно символов CTRL-s и CTRL-q. Как правило, без этих символов не обходятся там, где задействовано значительное количество устройств передачи данных между пользователем и самим компьютером (например, терминал взаимодействует с модемом, модем — с еще одним модемом, последний — с терминальной станцией, терминальная станция — с локальной сетью, локальная сеть — с компьютером, который использует удаленное подключение к другому компьютеру...). В общем случае, чем больше оборудования находится между вами и компьютером, тем больше вероятность того, что символы CTRL-s и CTRL-q используются для управления потоками. Отсюда следует, что они не доступны для редактора Emacs. Чтобы определить, имеете ли вы отношение к этой проблеме, просто вызовите редактор Emacs и нажмите клавиши [CTRL-s]. Если вы увидите в минибуфере приглашение Isearch:, то, скорее всего, все в порядке. В противном случае терминал как будто зависает: какую бы клавишу вы ни нажимали, он не будет реагировать. Оказывается, что в действительности вы дали указание какому-то из устройств больше не принимать входных данных. Чтобы выйти из этого состояния, нажмите [CTRL-q]. Теперь начнут выполняться команды, которые вы вводили. Простой способ справиться с этой проблемой — ввести команду [ESC-x] enable-f low- control. Данная команда позволяет использовать сочетания клавиш [CTRL-\] вместо GNU Emacs 507
32.13 [CTRL-s] и [CTRL-A] вместо [CTRL-q]. Конечно, следует помнить, что каждый раз, когда в обычных условиях применяется [CTRL-s], необходимо использовать [CTRL-\]. Если хотите сделать эту установку постоянной, добавьте в файл .emacs строку (enable-flow-control) Чтобы изменения в файле начали действовать, необходимо выйти из редактора Emacs и вновь запустить его. Не забывайте об этом. - BR, DC 32.13 Как убить свободное время Если вы не знаете, как убить время, редактор Emacs поможет вам и в этом. Он содержит множество интересных "приколов" типа несвязанного текста. Моей любимой является команда psychoanalyze-pinhead ("психоаналитическая булавка"). Она, в свою очередь, основана на программе doctor. Это вариация классической программы по искусственному интеллекту, называемой Eliza: компьютер, задавая вам вопросы, берет на себя роль психотерапевта. Вы отвечаете, и ваши ответы служат основой для новых вопросов. В действительности в этом нет ничего нового. Я помню эту программу еще со студенческой скамьи (начало семидесятых годов). И если вы считаете, что вам действительно не мешало бы обратиться к психотерапевту, попробуйте задать команду [ESC x] doctor [RETURN]. Кстати, это позволит вам сберечь деньги. Чтобы увидеть нечто действительно интересное, введите команду [ESC x] psychoanalyze- pinhead. Она использует встроенную программу doctor, снабжая ее цитатами из культового комикса "Zippy the Pinhead" ("Зиппи по кличке Булавка"). Впечатление просто фантастическое! Вот как выглядят в русском переводе те реплики и вопросы, которые выводит программа: Я — психотерапевт. Пожалуйста, расскажите, что вас беспокоит. Окончив ввод ответов, дважды нажимайте клавишу [RETURN]. ВЫ ЩЕЛКНУЛИ КАРЛА МОЛДЕНА ПО НОСУ!! Почему вы говорите, что я щелкнул Карла Молдена по носу? Занимаюсь ли я ВОРОВСТВОМ ИЗ МАГАЗИНОВ? Не потому ли вы спрашиваете о воровстве., что это в ваших планах? Продаете ли вы БУРОВЫЕ СКВАЖИНЫ компании NYLON OIL? Если да, то мы купим ДВЕ ДЮЖИНЫ!! Нажмите [CTRL-c], если вам это надоело. - ML 508 Часть пятая. Редактирование текста
33 Пакетное редактирование 33.01 Почему строковые редакторы до сих пор применяются В "старые времена", когда в качестве терминалов использовались принтеры, файлы редактировались построчно. Редакторы, которые позволяют перемещать курсор по экрану, предоставляя возможность выбирать текст, еще не были изобретены, как не было и экранов, где этот текст мог бы отображаться. В наши дни, когда имеется множество текстовых процессоров и редакторов WYSIWYG (принцип "что видишь, то и получишь"), новичкам легко думать о строковых редакторах как о странном пережитке. Возможно, так оно и есть, но эти пережитки обладают невероятными возможностями! Как известно, команды строковых редакторов годятся для создания сценариев, т.е. обладают способностью записывать свои действия, что позволяет помногу раз применять их при работе с различными файлами. Раз уж речь зашла о "пакетном редактировании", следует упомянуть о тех программах, которые для этого используются. • Редактор ed — самый первый строковый редактор UNIX. • Редактор ех, поддерживающий расширенное множество команд редактора ed. (Эти же команды активно используются в редакторе vi.) • Редактор sed — это редактор, который может выполняться только со сценариями [либо во время запуска ему можно задавать несколько коротких команд в качестве аргументов командной строки. — JP\. Хотя sed имеет много команд, подобных командам редакторов ed и ex, он от них существенно отличается (u.oi). • Редактор patch (ззм) — это специализированный редактор, разработанный для использования сценариев редактирования, создаваемых командой diff (28.oi). To же самое можно сделать с помощью редакторов ed и ех, но patch специально предназначен для этих целей. Количество редакторов очень велико. И здесь к месту было бы упомянуть о редакторах awk (зз.И) и perl (37.01), считающихся еще более мощными программами редактирования. -TOR 33.02 Создание сценариев редактирования Когда пользователь пишет сценарий, содержащий набор команд редактирования, то он задает последовательность действий, производимых при ручном редактировании. Однако если вы выполняете редактирование вручную, то ориентируетесь на результат, который получаете сразу же после ввода команды. Обычно существует команда "отменить", позволяющая вернуть текстовый файл в предыдущее состояние. В процессе изучения текстового редактора вырабатываются навыки внесения изменений безопасным и управляемым образом, по одному действию за один раз. Пакетное редактирование 509
33.03 Большинство пользователей, для которых термин "производительное редактирование" является чем-то новым, в большей мере испытывают чувство неуверенности и даже страх при написании сценариев для выполнения последовательности операций редактирования, чем когда производят эти операции вручную. Страх вызывает осознание того, что в результате автоматизации задания может произойти что-то необратимое. Прежде чем приступить к процессу создания сценариев для редакторов ех и sed, необходимо достаточно хорошо понять команды, чтобы можно было предсказывать результаты их выполнения. Другими словами, вы должны понимать причинные связи между сценарием редактирования и теми результатами, которые получаются. Постепенно можно будет разработать для себя методы создания и тестирования сценариев редактирования. Вы научитесь правильно применять эти методы и будете чувствовать себя гораздо увереннее, зная, что именно делает сценарий и как он это делает. Приведем по этому поводу несколько советов. 1. Прежде чем создавать собственный сценарий, тщательно проверьте с помощью утилиты grep исходный текстовый файл. 2. Начните работать с тестовым файлом, содержащим в качестве примера те слова и выражения, которыми вы интересуетесь. Выполните сценарий на этом примере, с тем чтобы проверить, работает ли он. Очень важно убедиться, что сценарий не работает там, где, по-вашему, он и не должен работать. Затем усложните задачу. 3. Будьте внимательны при работе, тестируя каждую добавляемую в сценарий команду. Сравнивайте файл результатов с исходным файлом, чтобы увидеть все изменения. Убедитесь сами, что в сценарии предусмотрены все варианты обработки. Сценарий может функционировать безукоризненно, если верны предположения о содержимом исходного файла, но само предположение может быть ошибочным. 4. Будьте практичны! Попытайтесь выполнить с помощью сценария все, что можете, но не забывайте, что невозможно создать сценарий, выполняющий абсолютно всю работу. Проанализируйте трудные ситуации и определите, как часто они повторяются. Зачастую небольшой оставшийся объем работы проще сделать вручную. [И еще одно пожелание: воспользуйтесь системой управления версиями (RCS) (20.12), предназначенной для хранения предыдущих версий файлов. Так проще будет отменять результаты редактирования. — JP\ — DD, из книги sed & awk издательства O'Reilly & Associates 33.03 Адресация строк Строковые редакторы можно использовать эффективнее, понимая главное, а именно: как выбирать (адресовать) строки, над которыми будут выполняться, команды сценария. В редакторах ed и ех команды воздействуют только на текущую строку. Сначала таковой является первая строка файла, а затем та, которая определилась в результате выполнения последней команды редактирования или перемещения (если только в начале команды не был указан адрес, определяющий другую строку или строки). В редакторе sed, если не указан адрес, большая часть команд применяется ко всем строкам. Большинство строковых редакторов адресуют строки тремя способами: • номерами строк; • шаблонами, содержащими регулярные выражения; • специальными символами. Адресовать можно отдельные строки или целый диапазон строк. Типы адресов, которые можно применять в редакторе ех, описаны в табл. 33.1. Таблица 33.1. Адресация строк в редакторе ех Адрес Область действия 1, $ Все строки файла % Все строки; то же самое, что и 1,$ 510 Часть пятая. Редактирование текста
33.04 Адрес Область действия х,у Строки с номерами из диапазона от х до у х;у Строки с номерами из диапазона от х до у; текущей становится строка с номером х 1 Начало файла О "Перед началом" файла. Используется для добавления текста перед начальной строкой: Or, xmO и т.д. Текущая строка л Строка с абсолютным номером л $ Последняя строка x-n л строк перед строкой с номером х х+л л строк после строки с номером х -л л предыдущих строк Предыдущая строка +л л последующих строк + Следующая строка 'х Строка, обозначенная маркером х. (Чтобы обозначить строку маркером, воспользуйтесь командой кх) ' ' Предыдущий маркер /шаблон/ Следующая строка, соответствующая шаблону ?шаблон? Предыдущая строка, соответствующая шаблону Если адрес определяет диапазон строк, его формат будет таким: х,у где х и у — это первая и последняя адресуемые строки. В файле строка х должна предшествовать строке у. - TOR, DG, JP 33.04 Полезные команды редактора ех Многие команды строкового редактора не особенно полезны в сценариях. Наиболее часто используются две команды: s (заменить), предназначенная для замещения одного шаблона другим, и d (удалить), выполняющая удаление одной или нескольких строк. Но иногда может понадобиться и вставить текст из сценария. (Сценарии редактирования, созданные командой diff (2S.09), делают использование команд вставки, добавления, удаления и изменения почти невозможными.) И конечно, вам никак не обойтись без команд записи файла и выхода из редактора. Ниже приводится синтаксис большинства команд, которые могут встретиться в сценариях редактора ex. (Редактор ed "понимает" сокращенные версии некоторых команд.) Параметры, указанные в квадратных скобках, не являются обязательными; не набирайте ни [, ни ]. (Двоеточие в начале строки — это символ команд редактора ех, который используется при их выполнении из редактора w; в сценарии двоеточие следует опускать.) append [адрес] а[\] текст Добавить текст по указанному адресу или по текущему, если данный параметр опущен. Если необходимо изменить значение опции autoindent, которое будет использоваться во время ввода, добавить параметр !. Например, если опция autoindent была включена, то при наличии параметра ! она будет выключена. Пакетное редактирование 511
33.04 change [адрес] c[\] текст Заменить указанные строки текстом. Добавьте параметр !, если значение опции autoindent необходимо изменить. сору [адрес] со место_назначения [адрес] t место_назначения Скопировать строки, определяемые адресом, по указанному адресу места_на- значения. :1,10 со 50 :l,10t50 delete [адрес] d [буфер] Удалить строки, определяемые адресом. Если указан буфер, сохраните или добавьте текст в именованный буфер. :/Part I/,/Part II/-ld Удалить до строки над "Part II" :/main/+d Удалить Строку под "main" :.,$/d Удалить все от текущей строки до последней global [адрес] д[!] /шаблон/ [команды] Выполняет команды над всеми строками, содержащими шаблон, или, если указан адрес, над всеми строками из диапазона, заданного адресом. Если команды не указаны, выводятся на экран все строки, содержащие /шаблон/. (Исключение: строки не выводятся, если команда задается из редактора w и ей предшествует двоеточие (:). В таком случае необходимо добавить параметр р, как показано ниже, во втором примере.) Если используется параметр !, данная команда выполняет команды над всеми строками, не содержащими шаблон. :g/Unlx/ :g/Unix/p :g/Name:/s/tom/Tom/ insert [адрес] i[!] текст Вставить текст в строку перед строкой с указанным адресом или в текущую строку, если адрес не указан. Добавьте параметр !, и вы сможете переключить установку опции autoindent, которая будет использоваться во время ввода текста. move [адрес] m место_назначения Переместить строки, определяемые адресом, по адресу места_назначения. :.,/Note/m /END/ Переместить блок после строки, содержащей слово "END" print [адрес] р [счетчик] Вывести на экран строки, определяемые адресом. Счетчик указывает, сколько строк вывести, начиная со строки, заданной адресом. :100;+5р Отобразить сотую и следующие пять строк quit q[!] Завершить текущий сеанс редактирования. Для отмены изменений, выполненных после последнего сохранения, можно воспользоваться параметром !. Если строка вызова редактора содержит и другие файлы, к которым не было обращения во время сеанса редактирования, завершите работу по команде q! или дважды нажмите q. 512 Часть пятая. Редактирование текста
33.04 read [адрес] г файл Скопировать текст из файла в строку, следующую после строки с указанным адресом. Если файл не задан, используется текущий файл. :0r $HOME/data Вставить содержимое файла data в начало текущего файла read [адрес] г !команда Поместить вывод команды в строку, следующую после строки с указанным адресом. cal 4S.06 : $r ! cal Вставить вывод команды cal (календарь) в конец файла source so файл Прочитать из файла команды редактора ех и выполнить их. :зо $НОМЕ/.ехгс substitute [адрес] s [/шаблон/строка_замещения/] [опции] [счетчик] Заменить первый экземпляр шаблона в каждой из указанных строк стро- кой_замещения. Если шаблон и строка_замещения опущены, повторяется последняя замена. Счетчик определяет число строк, к которым применяется команда замены, начиная со строки с указанным адресом. Опции с Запрос на подтверждение перед каждым изменением. ч Заменить все экземпляры шаблона в каждой строке. Р Вывести последнюю строку, к которой применялась команда замены. : 1,10s/yes/no/g Заменить в первых десяти строках QjgjO :%s/[Hh]ello/Hi/gc Глобальная замена с подтверждением перед каждой заменой \U 30.15 :s/Fortran/\US/ 3 В трех следующих строках все буквы слова Fortran преобразовать в прописные write [адрес] w[!] [ [»] файл] Записать строки с указанным адресом в файл или записать все содержимое буфера редактирования, если адрес не указан. Если опущен и параметр файл, сохранить содержимое буфера в текущем файле. При использовании выражения » файл записать содержимое буфера в конец существующего файла. Параметр ! заставляет редактор перезаписывать текущее содержимое файла. :l,10w name_li3t Скопировать первые 10 строк в файл name_list :50w » name_list Теперь добавить в этот файл 50-ую строку write [адрес] w [команда Направить строки с указанным адресом или все содержимое буфера, если адрес не указан, на стандартный ввод (u.oi) команды. :l,10w !spell Передать первые 10 строк команде spell :w !lpr Распечатать весь буфер, задав команду lpr - TOR, DG Пакетное редактирование 17 9-171 523
33.05 33.05 Выполнение сценариев редактирования из редактора vi Поскольку редактор vi создан на основе строкового редактора ех, пользователь получает в свое распоряжение и все возможности построчного редактирования. Обычно команды поочередно вводятся в командной строке. Единственным исключением является файл .ехгс ро.об), который, по сути, представляет собой список команд редактора ех, выполняемых при его запуске, т.е. служит сценарием редактора. По-видимому, далеко не все пользователи знают, что последовательность команд редактора ех можно сохранить в любом файле и выполнять ее по команде : so (ззм). Так, Брюс Барнетт (Brace Barnett) использует этот прием для подготовки редактора к редактированию программ на языке FORTRAN pui). Вообще-то редактор sed больше подходит для пакетного редактирования универсального характера, в частности для выполнение ряда повторных замен во множестве файлов, тогда как команда :so чаще используется для выполнения установок редактора, а не для редактирования. Тем не менее возьмите себе на заметку: всякий раз, когда возникает необходимость использовать одни и те же команды помногу раз подряд, подумайте о сценарии). - TOR 33.06 Изменение нескольких похожих файлов _.у,/ Команда diff может создать сценарий редактирования (2н.щ, который потом будет использо- jf^T^ ваться либо с редакторами ed и ех, либо с программой patch (зз.оя). Указанные редакторы и Кш программа будут применять одни и те же команды редактирования ко всем копиям одного и того же файла. Особенно это удобно, когда при работе со многими копиями большого [33.08] файла, разбросанными в сети или на множестве дисков, необходимо выполнить одни и те же незначительные изменения. Вместо рассьшки новых копий файла достаточно ввести команду diff и создать несложный сценарий для обновления всех больших файлов. Продемонстрируем сказанное на примере. Я собираюсь изменить программу, которая называется pqs.c. Чтобы сделать те же изменения в копии этого файла, называющейся remote-pqs.c (она должна находиться на удаленном компьютере), я воспользуюсь командой diff и редактором ех: & 1% ср pqs.c remote-pqs.c ■■ к^ 2% ср pqs.c pqs.c.new 3% vi pqs.c.new 4% diff pqs.c pqs.c.new [28.07] 106,107C106 < fprintf(stderr, < "%s: quitting: not able to %s your .pq_profile file.\n", > fprintf (stderr, *'%s: quitting: can't %s your .pq_profile file.\n", 390a390 > "WARNING:", 5% diff -e pqs.c pqs.c.new > edscr 6% cat edscr 390a "WARNING:", 106,107c fprintf(stderr, "%s: quitting: can't %s your .pq_profile file.\n", » 13.01 7% echo w » edscr 8 % ed remote-pqs.с < edscr 19176 19184 9% diff pqs.c.new remote-pqs.c 10% 514 Часть пятая. Редактирование текста
33.07 По приглашению 1% я имитирую создание "удаленной" копии файла pqs.c, по приглашению 2% создаю еще одну копию этого файла, а по приглашению 3% редактирую ее. В приглашении 4% содержится команда diff, которая отображает произведенные изменения. Затем по приглашению 5% я выполняю команду diff -e (2s.oi/, полученные результаты запоминаются в файле edscr, который выводится на экран по приглашению 6%. Важным является приглашение 7%, так как команда diff -e не вставляет в файл сценария команду w. Эта команда будет служить редактору ed указанием записать изменения в файл. Я использую команду echo w (в.об), чтобы добавить в файл сценария команду w. По приглашению 8% я задаю редактору ed имя "удаленного" файла в виде аргумента командной строки и файл сценария как стандартный ввод. По приглашению 9% выполняю команду diff, которая показывает, что две версии файла не отличаются друг от друга. - JP 33.07 Пакетное редактирование: как избежать ошибок, когда нет соответствия шаблону Вопрос. В моем сценарии интерпретатора Bourne shell (44.oi) для редактирования ряда файлов вызывается редактор ed: site=something cmty=somethingelse for i in filel file2 file3 do ed 5i « end l,\$s/patl/$site/g 1,\$s/pat2/$cmty/g w q end done Редактор работает превосходно, за исключением случая, когда в одном из файлов нет подстроки path Он не обновляет данный файл, даже если может найти в нем шаблон pat2. Все другие файлы редактируются как положено. Что же делать? Ответ. При возникновении ошибки, в том числе и ошибки типа no matches (нет соответствия), редактор ed пытается отказаться от всех непрочитанных команд. Если вы работаете с ним "вручную", такого не происходит, но если редактор берет входные данные из файла, то при неудачном поиске появляется признак конца файла — символ EOF. Вы можете удалить команду q и увидеть то же самое, когда редактор ed автоматически завершает работу по достижении конца файла. Из подобной ситуации нетрудно найти выход (команда д, в отличие от команды s, не выдает сообщений об ошибках, если нет соответствия шаблону): ed - $i «end g/patl/s//$site/g g/pat2/s//$cmty/g w end Флаг — (тире) подавляет появление двух чисел, которые редактор ed обычно выводит при чтении и записи файлов. Эти числа обозначают количество символов в файле, и в данном случае они не столь важны. [Как объяснял Крис, команда q в исходном сценарии лишняя. — JP] — СТ, из телеконференции comp.unix.questions в Usenet, 16 моя 1989 г. = 6.08 Гот 9.12 « 8.18 \8.14 Пакетное редактирование 17* 515
33.08 33.08 Ошибки пакетного редактирования при обработке больших файлов Редактор ed используется с файлами сценариев при выполнении глобального редактирования. Но многие версии данного редактора не могут редактировать большие файлы. Для этого больше подходит редактор ех, но в нем тоже есть свои ограничения. К тому же следует уточнить, файл какого размера принято считать большим. Это зависит от версии редактора. Большинство версий редактора ed, с которыми мне приходилось иметь дело, не могут работать с файлами, содержащими более 100000 знаков. Таких ограничений на размер файла нет в редакторе sed (34М), хотя вам придется каким-то образом запоминать его выходные данные (34.оз) и изменять свой сценарий редактирования, чтобы он соответствовал требованиям редактора sed* Вот пример сообщения об ошибке, возникающей при работе с файлом большого объема: % cat edscr s/Unix/UNIX/g w % ed - words < edscr ? % Вы, надо полагать, захотите узнать, что обозначает ? (вопросительный знак). Отвечаю: таким "многословным" способом редактор ed сообщает, что произошла какая-то ошибка. От этого мало заметного и несколько непонятного сообщения не следует ожидать ничего хорошего, особенно когда вы пишете сценарий интерпретатора shell, который. в цикле редактирует несколько файлов. Можно просто не заметить ошибки или не суметь понять, какой файл порождает проблему. Убедитесь, что в сценарии выполняется проверка на наличие ошибок! Однако следует заметить, что редактор ed, к огорчению программистов, может не выдавать код ошибки, который можно было бы проверить. Правда, имеются способы обойти и эту ситуацию (46.09). Когда команда ed - завершается успешно, она ничего не выводит на экран. Следовательно, чтобы обнаружить ошибку, достаточно проверить, не посылается ли что-нибудь в выходные потоки stdout и stderr. В следующем фрагменте сценария интерпретатора Bourne shell показывается, как это сделать (переменная $filename (6.08) содержит имя файла пользователя): edout=""ed - $filename <edscr 2>&1"" if [ -n "$edout" -o $? -ne 0 ] then echo "$edout" 1>&2 echo "QUITTING: 'ed - $filename <edscr" bombed?!?" 1>&2 exit 1 fi - JP Программа patch: обновление файлов, обработанных командой diff Подобно всем другим широко используемым программам Ларри Уолла (включая программу perl (37.01), сценарий конфигурации программного обеспечения под названием Configure и программу чтения новостей т), программа patch подкупает фантастической "понятливостью"/ Исходными данными для нее могут служить и любой листинг результатов работы команды diff (28.01), и сценарий редактирования, который создается посредством опции -е (28.09). Программа patch определяет, что следует сделать, и обновляет файл, выдавая на протяжении всего времени работы веселые комментарии по поводу происходящего: % patch < testfile.diff Hmm... Looks like a normal diff to me... * По умолчанию команды редактора ed применяются к текущей строке. Команды редактора sed являются глобальными. Относительная адресация строк (наподобие -5) в редакторе sed не работает. 516 Часть пятая. Редактирование текста 2>&1 45.21 П 4430 $! 44.07 33.09 patch
33.09 (Гм-м... Кажется diff сделала все нормально...) File to patch: testfile (Файл для внесения изменений: testfile] Patching file testfile using Plan A... (Модифицируется файл testfile по плану А...) Hunk #1 succeeded at 2. (Фрагмент №1 успешно обработан во второй строке.) done (готово) Как Ларри однажды заметил, программа patch сделала очень много для "изменения культуры обработки данных". В настоящее время почти все свободно распространяемое программное обеспечение обновляется посредством различных "заплат", а не путем рассылки полных его вариантов. Программа patch достаточно "разумна", чтобы отбрасывать любой мусор в начале или в конце файла (например, заголовки почтовых сообщений или цифровые подписи), благодаря чему файл с исходной программой может быть обновлен, скажем, за счет поданного на вход программы patch почтового сообщения,-содержащего результаты обработки командой diff старой и новой версий файла. Приведем небольшой перечень того, что еще программа patch способна сделать: • определить имя файла, который следует обновить, и выполнить обновление, не запрашивая указаний пользователя (обычно это происходит только тогда, когда файл с результатами работы команды diff содержит не только различающиеся строки, но и некоторый контекст (28.аз), сгенерированный командой diff -с); • если файл с нужным именем не найден, разыскать подходящий SCCS- иди RCS-файл ро.п) и проверить его; • провести работу с листингом результатов выполнения команды diff, не соответствующим реальному состоянию файла. Это дает возможность данной программе обновить файл, который был изменен получателем уже после обработки командой diff, ■ • сохранить любые неиспользованные фрагменты файла, созданного командой diff, в файле с таким же именем, какое имеет обрабатываемый ею [программой patch] файл, но с добавлением к нему суффикса .rej (reject — отвергать); • продублировать обрабатываемый файл, добавив суффикс .orig к имени исходного файла; • распознать, что ее входные данные на самом деле могут применяться к различным файлам, и выполнить обработку каждого из них отдельно. Благодаря этому с помощью, например, файла "заплаты", где содержатся результаты работы команды diff по каждому файлу каталога, может быть обновлен целый каталог. (Между прочим, воспользовавшись опцией -d, программе patch можно дать указание перед началом работы сделать указанный каталог текущим); • определить или, по крайней мере, сделать предположение о том, что обработка может быть выполнена некорректно из-за того, что при указании аргументов команды diff были перепутаны старый и новый файлы. Ларри говорит: "Я боюсь, что это может происходить время от времени — такова уж человеческая природа". (Опция -R программы patch будет вынуждать ее производить обработку с учетом этого обстоятельства. Что-что, а наблюдать, как программа patch принимает решение о том, как же ей поступить, если файлы оказываются несовместимыми, — это настоящее развлечение!) Для программистов указанная программа представляет интерес уже хотя бы потому, что из ее текста они могут узнать, как обрабатываются входные данные неопределенного характера и как "переложить на компьютер черновую работу". Хотя, если вы программист, то наверняка уже знакомы с программой patch. И последнее замечание. Программа patch настолько полезна, что поставляется со многими UNIX-системами. Прежде чем инсталлировать ее с компакт-диска, проверьте, нет ли ее в вашей системе. [Мне приходилось слышать о версиях этой программы, поставляемых с ошибками и даже вовсе не работоспособных. Рекомендую сравнить программу patch, имеющуюся на диске, с той, которая есть у вас. — JP\ -TOR Пакетное редактирование 517
33.10 33.10 Быстрая глобальная замена из командной строки с помощью программы qsubst Пользователям в нашем офисе часто приходится делать глобальную замену во многих файлах. Мы советуем им для этой цели применять редактор sed (34.24). Однако синтаксис команд редактора sed для новичков (а иногда и для опытных пользователей!) слишком сложен. НВ качестве альтернативы попробуйте задействовать программу qsubst, которая записана на компакт-диске. Это достаточно простая программа замены по запросу, которой может пользоваться любой. Например, чтобы в файлах chOl и ch02 заменить строку Unix строкой qsubst UNIX, достаточно ввести такую команду: % qsubst Unix UNIX chOl ch02 Программа выводит строку, в которой необходимо сделать замену, вместе с контекстом. Другими словами, строка, содержащая заменяемую подстроку, выводится вместе с предшествующей ей и последующей строками файла. Подстрока, которая будет заменена, подчеркивается: Unlike emacs, vi is available on every Unix system. So you can think of vi as the Чтобы дать согласие на замену, нажмите клавишу [ПРОБЕЛ]. Затем программа попросит дать согласие на замену следующего вхождения подстроки Unix. В ответ вы можете ввести ! (восклицательный знак), давая таким образом согласие на замену данного экземпляра подстроки Unix и всех последующих ее экземпляров в текущем файле. Желая отказаться от замены, нажмите клавиши [CTRL-g]. Замену всех вхождений данной подстроки без ввода подтверждения можно также "поручить" программе qsubst. Для этого при вызове программы qsubst в командной строке следует указать опцию -noask: % qsubst Unix UNIX -noask chOl ch02 (file: chOl) (file: ch02) Программа редактирует оба файла, не спрашивая согласия на замену. Понятно, что программа qsubst не обладает такими же возможностями, как редактор sed. Она не "понимает" регулярных выражений и поэтому используется для замены только простых строк. Но совершенно очевидно, что программа qsubst — это мощное средство для пользователей, которые хотят выполнить несложную глобальную замену, не изучая редактор sed. Одно предупреждение относительно работы с программой qsubst. если программа во время выполнения будет прервана (например, посредством комбинации клавиш [CTRL-c]), некоторые установки терминала, по всей вероятности, изменятся. Если такое случится, попытайтесь воспользоваться советами, содержащимися в параграфе 42.04. - LM 33.11 Краткий справочник: утилита awk В этом параграфе рассматриваются также утилиты nctwk и gawk (зз.П). При описании синтаксиса команд значения в квадратных скобках, за исключением индексов массивов, являются необязательными. Если же необязательные параметры все-таки используются, набирать [ и ] не следует. Синтаксис командной строки Утилиту awk можно вызвать двумя способами: awk [опции] 'сценарий' [переменная=значение][файл(ы)] awk [опции] -Е файл_сценария [переменная=значение] [файл (ы)] Сценарий обычно указывается непосредственно в командной строке или в файле_сценария, а затем задается с помощью опции -/ В большинстве версий программы опцию -/можно 518 Часть пятая. Редактирование текста
33.11 использовать по нескольку раз. Переменной в командной строке может быть присвоено значение в виде строки, переменной интерпретатора shell ($имя) или команды замены С команда"), но доступным оно становится только после того, как будет прочитана строка с входными данными (т.е. после выполнения оператора BEGIN). Утилита awk обрабатывает один или несколько файлов. Если ни один файл не указан (или если задана опция -), она читает данные из стандартного ввода оз.оц. Другие опции утилиты awk перечислены ниже. -Fc Устанавливает в качестве разделителей полей символ с. Это то же самое, что и установка системной переменной FS. Утилита nawk допускает, чтобы значением с было регулярное выражение (2в.т). Каждая запись (по умолчанию одна строка входных данных) делится на поля пробельными элементами (пробелами или знаками табуляции) или какими-либо другими разделителями полей, определенными пользователем. На поля ссылаются с помощью переменных $1, $2,...,$л. Переменная $0 используется для ссылки на всю запись. Например, чтобы напечатать в отдельных строках первые три поля (разделяются двоеточием), следует задать следующую команду: % awk -F: '{print $1; print $2; print $3}' /etc/passwd -v переменная^ Присваивает значение указанной переменной. Присваивание разре- эначение шается до начала выполнения сценария. (Эту опцию имеет только утилита nawk.) Шаблоны и процедуры Сценарий утилиты awk состоит из шаблонов и процедур: шаблон (процедура) И шаблон, и процедура являются необязательными. Если опущен параметр шаблон, процедура применяется ко всем записям. Если опущен параметр [процедура], печатаются записи, которые соответствуют шаблону. Шаблоны Шаблон может состоять из следующих компонентов: /регулярное выражение/ выражение отношения выражение, задающее операцию сравнения с шаблоном BEGIN END • Выражения образуются взятыми в кавычки строками, числами, операциями, функциями, объявленными переменными или любыми предопределенными переменными, которые описаны далее, в параграфе "Системные переменные". • В регулярных выражениях применяется расширенный набор метасимволов, как описано в параграфе 26.04. Кроме того, символы А и $ могут использоваться для ссылки соответственно на начало и конец поля, а не на начало и конец записи (строки). • В выражениях отношения используются операции отношения, перечень которых приводится ниже в параграфе "Операторы". Операции сравнения могут выполняться как над строками, так и над числами. Например, с помощью выражения $2 > $1 выбираются записи, в которых второе поле больше первого. • В выражениях, содержащих шаблоны, используются операции ~ (соответствовать) и ! ~ (не соответствовать). • Шаблон BEGIN позволяет определять процедуры, которые выполняются до того, как обрабатывается первая запись входных данных. (Обычно при этом устанавливаются глобальные переменные.) • Шаблон END позволяет определять процедуры, которые будут выполняться после того, как последняя запись входных данных будет прочитана. Пакетное редактирование 519
33.11 За исключением шаблонов BEGIN и END, все остальные шаблоны могут объединяться булевыми операторами | I (ИЛИ), && (И) и ! (НЕ). Используя шаблоны, разделенные запятыми, можно также указывать диапазон строк: шаблон, шаблон Процедуры Процедура обычно состоит из одной или нескольких команд, функций или выражений присваивания, разделенных символом новой строки или точкой с запятой (;) и взятых в фигурные скобки ({}). Команды делятся на четыре группы: • команды присваивания значений переменным и массивам; • команды вывода; • встроенные функции; • команды управления ходом выполнения процедуры. Простые примеры шаблонов и процедур 1. Вывести первое поле каждой строки: { print $1 } 2. Вывести все строки, содержащие шаблон: /шаблон/ 3. Вывести первые поля строк, содержащих шаблон: /шаблон/I print $1 ) 4. Вывести записи, содержащие более двух полей: NF > 2 5. Интерпретировать входные записи как группу строк, оканчивающуюся пустой строкой: BEGIN { FS = "\n"; RS = ""; ) ( ... обработка записей...) 6. Вывести поля в обратном порядке, но только для тех строк, первое поле которых совпадает с заданной строкой URGENT: $1 ~ /URGENT/( print $3, $2 } 7. Сосчитать и вывести число вхождений шаблона: /шаблон/ { ++х ) END ( print x } 8. Сложить значения во втором столбце и вывести их сумму: {total += $2 ) END { print "итоговая сумма в столбце:", total } 9. Вывести строки, содержащие менее 20 символов: length($0) < 20 10. Вывести все строки, которые начинаются подстрокой Name: и содержат ровно 7 полей: NF == 7 && /"Name:/ Системные переменные Утилита nawk поддерживает все переменные утилиты awk, а утилита gawk — переменные как утилиты nawk, так и утилиты awk. 520 Часть пятая. Редактирование текста
33.11 Версия awk nawk Переменная FILENAME FS NF NR OFMT OFS ORS RS $0 $n ARGC ARGV ENVIRON FNR RSTART RLENGTH SUBSEP Содержимое переменной Имя текущего файла Разделитель полей (по умолчанию — пробельные символы) Число полей в текущей записи Номер текущей записи Формат вывода для чисел (по умолчанию — %. 6д) Разделитель полей при выводе (по умолчанию — пробел) Разделитель записей при выводе (по умолчанию — символ новой строки) Разделитель записей (по умолчанию — символ новой строки) Входная запись целиком я-ое поле в текущей записи; разделителем является значение переменной FS Количество аргументов в командной строке Массив, содержащий аргументы командной строки Ассоциативный массив переменных среды Подобна переменной NR, но относится к текущему файлу Первая позиция в строке, где функцией match найдено соответствие шаблону Длина строки, в которой функцией match найдено соответствие шаблону Символ, являющийся разделителем индексов массива (по умолчанию — \34) Операторы В следующей таблице перечислены знаки операций утилиты awk в порядке возрастания приоритета. Обозначение = += -= *= /= %= А= "Р • 1 1 && •г <<=>>= != == (пробел) + - * / % + - ! А + + $ Операция Присваивание (Л= есть только в nawk и gawk) Условное выражение в стиле языка С (nawk и gawk) Логическое ИЛИ Логическое И Соответствие и несоответствие регулярному выражению Операции отношения Конкатенация Сложение и вычитание Умножение, деление и деление по модулю Унарные плюс и минус, логическое отрицание Возведение в степень Увеличение или уменьшение на единицу, префиксные и постфиксные операции Ссылка на поле Присваивание значений переменным и массивам При присваивании переменным значения используется знак равенства (=): FS="," Переменным может быть присвоено значение выражений, которые содержат операторы +, -, *, / и % (деление по модулю). Пакетное редактирование 521
33.11 Массивы можно создать с помощью функции split, или же просто назвать их в операторах присваивания. Элементы массивов задаются индексами (массив[\], ..., массив[п]) или именами. Например, для подсчета числа вхождений шаблона необходимо использовать такой сценарий: /шаблон/{ массив["шаблон"]++ ) END { print массив["шаблон"] ) Перечень команд по группам О том, как классифицируются команды утилиты awk, можно судить по следующей таблице. Арифметические функции atan2* cos* exp int log rand* sin* sqrt srand* Строковые функции gsub* index length match* split sub* substr tolower* toupper* Операторы управления выполнением break continue do/while* exit for if return* while Обработка ввода-вывода close* delete* getline* next print printf sprintf system* break close * В первоначальной версии утилиты awk отсутствуют. Сводка команд в апфавитном порядке В представленном ниже списке в алфавитном порядке перечислены все операторы и функции, которыми пользователь может распоряжаться в утилитах awk, nawk и gawk. Если не сказано обратное, рассматриваемые операторы и функции доступны во всех версиях. Новые операторы или функции, введенные в утилиту nawk, доступны также в утилите gawk. atan2 atan2(у, х) Возвращает арктангенс значения у/х, в радианах, (nawk) Выход из цикла while, for или do. close(имя_файла) close (.команда) В некоторых реализациях утилиты awk можно иметь только десять одновременно открытых файлов и один неименованный канал. Однако утилита awk предоставляет оператор close, позволяющий закрыть файл или канал. В операторе close в качестве аргумента задается то же выражение, которое использовалось для открытия файла или канала, (nawk) Немедленно начинает следующую итерацию цикла while, for или do. cos(х) Возвращает косинус х, в радианах, (nawk) delete массив[элемент] Удаляет элемент указанного массива, (nawk) do тело_цикла while (выражение) Оператор цикла. Выполняет операторы тело_цикла, затем вычисляет выражение. Если выражение истинно, снова выполняет операторы тело_цикла. При необходимости задействовать несколько команд их имена следует поместить в фигурные скобки ({}). (nawk) continue delete do 522 Часть пятая. Редактирование текста
33.11 exit exit[выражение] He выполнять оставшиеся команды и не читать новых входных данных. Будет запущена процедура END, если таковая присутствует. Значение выражения, если оно задано, становится кодом завершения (44.07) утилиты awk. ехр ехр(аргумент) Возвращает натуральную экспоненту от аргумента. for for ([начальное_выражение]; [проверочное_выражение]; [инкрементное_выражение]) команда Цикл в стиле языка С. Обычно начальное_выражение присваивает начальное значение переменной-счетчику цикла. Проверочное_выражение — это выражение, в котором задается операция отношения. Оно вычисляется каждый раз перед выполнением команды. Пока проверочное_выражение ложно, цикл выполняется. Инкрементное_выражение используется для увеличения значения счетчика в каждом цикле. Последовательность команд должна быть взята в фигурные скобки ({}). Пример for (i - 1; i <= 10; i++) printf "Element %d is %s.\n", i, array[i] for for (элемент in массив) команда Для каждого элемента в ассоциативном массиве выполняется команда. Если команд более одной, их необходимо поместить в фигурные скобки ({}). Элемент массива адресуется так: массив [ элемент]. getline getline [переменная] [<файл] или команда | getline [переменная] Прочитать следующую строку входных данных. Первоначально утилита awk не позволяла открывать несколько потоков входных данных. В первой форме команды входные данные читаются из файла, а во второй — из стандартного входного потока. В обоих случаях данные читаются построчно, и в каждый момент времени, когда выполняется команда, читается следующая строка входных данных. Введенная строка присваивается переменной $0 и разбивается на поля. При этом устанавливаются переменные NF, NR и FNR. Если указана переменная, результат присваивается этой переменной, а значение $0 не изменяется. Команда getline на самом деле является функцией и возвращает такие значения: 1, если читает запись успешно; 0, если достигается конец файла; -1, если по какой-либо причине операция заканчивается неудачно, (nawk) gsub gsub(r, s[, t]) В строке t каждая подстрока, которая соответствует регулярному выражению г, заменяется подстрокой s. Возвращается число произведенных замен. Если не указан параметр t, по умолчанию берется переменная $0. (nawk) if if (условие) команда [else команда] Если условие истинно, выполняется команда (или команды), в противном случае выполняется команда (или команды) в операторе else (если он указан). Пакетное редактирование 523
33.11 Условием может быть выражение, которое использует произвольные операции отношения <, <=, ==, !=, >=, >, а также операции сравнения с шаблоном ~ или !~ (например, if ($1 ~ /[Аа] .*[Zz/)). Последовательность команд должна помещаться в фигурные скобки ({}). index index{строка, подстрока) Возвращает позицию подстроки в строке или 0, если подстрока не найдена. int int{аргумент) Возвращает целую часть значения аргумента. length length{аргумент) Возвращает длину аргумента, log log(аргумент) Возвращает натуральный логарифм аргумента. match match(s, r) Находит в строке s подстроку, которая соответствует шаблону, заданному регулярным выражением г, и возвращает либо данные о позиции в строке s, где начинается подстрока, либо 0, если не найдено вхождения шаблона в строку. Устанавливает значения переменных RSTART и RLENGTH. (nawk) next Читает следующую строку входных данных и начинает новый цикл выполнения операторов сценария. print print [аргументы] [место_назначения] Выводит аргументы вместе с последующим символом новой строки. Аргументы — это одно или несколько полей, но ими могут быть одна или несколько предопределенных переменных или произвольных выражении. Если аргументы не заданы, выводится переменная $0 (текущая строка входных данных). Символьные строки должны заключаться в кавычки. Поля выводятся в^том порядке, в котором перечислены в аргументах. Если в списке аргументов они разделяются запятыми, то на выходе — символами, задаваемыми переменной OFS, если пробелами, то на выходе конкатенируются (объединяются). Место_на- эначения — это выражение для переадресации стандартного вывода в файл или канал (например, > имя_файла). printf printf формат [, выражения] [место_назначения] Оператор форматированного вывода. Поля или переменные могут быть отформатированы в соответствии с инструкциями, заданными в аргументе формат. Число выражений должно соответствовать числу, определяемому операторами форматов. В аргументе формат используются те же соглашения, что и в операторе printf языка С. Ниже перечислено несколько наиболее общих параметров форматирования: %s вывод строкового значения %d вывод десятичного числа %n.m£ число с плавающей запятой, где л — это общее количество цифр, am — длина дробной части %[-]лс параметр л определяет минимальную длину поля для параметра форматирования с, в то время как параметр - задает выравнивание поля по левой границе; в противном случае используется выравнивание по правой границе 524 Часть пятая. Редактирование текста
33.11 Аргумент формат может содержать встроенные последовательности управления выводом, наиболее общими из которых есть \п (символ новой строки) и \t (символ табуляции). Место_назначеиия — это выражение для переадресации стандартного вывода в файл или канал (например, > имя_файла). Пример {printf "Сумма значений в строке %s равна %d.\n", NR, $1+$2} Задание входных данных в виде 5 5 приведет к выводу следующего результата: Сумма значений в строке 1 равна 10. rand rand() Генерирует случайные числа, равномерно распределенные в интервале [0,1]. Эта функция возвращает одинаковую последовательность чисел после каждого выполнения сценария, если генератор случайных чисел не инициализирован функцией srandQ. (nawk) return return[выражение] Используется в конце функций, определенных пользователем, для выхода из них. Возвращает значение выражения, если оно задано. sin sin(x) Возвращает синус х, в радианах, (nawk) split split{строка, массиве, разделитель]) Разбивает строку на элементы массива (массив [1], ..., массив [л]) с помощью разделителей. (Для утилиты nawk разделителем может служить регулярное выражение.) Если разделитель не указан, в качестве такового используется значение, заданное переменной FS. Возвращает количество созданных элементов массива. sprintf sprintf {формат [, выражения]) Возвращает значение выражения (выражений), используя заданный формат (см. описание функции printf). Данные форматируются, но не выводятся. sqrt sqrt{аргумент) Возвращает квадратный корень аргумента. srand srand{выражение) Использует выражение как новое начальное значение для инициализации генератора случайных чисел. По умолчанию в качестве начального значения используется текущее время. Возвращает прежнее начальное значение, (nawk) sub sub(r, s[, t]) В строке t первая подстрока, которая соответствует регулярному выражению г, заменяется подстрокой s. Возвращается значение 1, если операция выполнилась успешно, и 0 — в противном случае. Если не указан параметр t, то по умолчанию берется переменная $0. (nawk) substr substr{строка, ml, n]) Возвращает подстроку строки, начинающуюся с позиции m и содержащую следующие п символов. Если п опущено, включаются все символы до конца строки. Пакетное редактирование 525
33.12 system system(команда) Выполняет указанную команду UNIX и возвращает ее код завершения (44.т), указывающий на успешное выполнение (значение 0) или на неблагоприятный исход (ненулевое значение). Выводимые командой данные не доступны для обработки в сценарии, {nawk) tolower tolower(строка) Преобразует в строке все прописные буквы в строчные и возвращает новую строку, (nawk) toupper toupper (строка) Преобразует в строке все строчные буквы в прописные и возвращает новую строку, (nawk) while while (условие) команда Выполняет команду, пока истинно условие. (Допустимые условия описаны в пояснениях к оператору if.) Последовательность команд должна помещаться в фигурные скобки ({)). — DG, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates 33.12 Версии утилиты awk gawk Утилита awk появилась в седьмой версии UNIX и с тех пор сопровождает все стандартные системы. В 1985 году авторы утилиты awk расширили ее синтаксис, добавив множество полезных возможностей. К сожалению, эта новая версия несколько лет использовалась только в компании AT&T. Она стала постоянной частью разработанной в AT&T системы System V с момента появления версии 3.1. С той же поры утилита известна под именем nawk (new awk — новая awk); старые ее версии все еще существуют под первоначальным именем. К сожалению, утилита nawk доступна не во всех системах. Радует лишь тот факт, что разработанная в Free Software Foundation GNU-версия awk, получившая имя gawk, поддерживает все свойства nawk. В дальнейшем мы будем исходить из предположения, что все, справедливое для утилиты nawk, справедливо и для утилиты gawk, если специально не оговорено обратное. Сценарий, написанный для nawk, полностью совместим с утилитой gawk. Для того чтобы воспользоваться одним из лаи>А:-сценариев, иметь в системе утилиту nawk не обязательно — достаточно просто изменить сценарий, предоставив ему возможность запускать утилиту gawk. Есть несколько областей, где утилита gawk проявляет специфические свойства. Последние версии nawk поддерживают многие, но не все из этих свойств; предполагается, что остальные не представляют особого интереса. В любом случае это не должно иметь для вас значения, поскольку мы поместили утилиту gawk на прилагаемый к данной книге компакт-диск. В самой книге ни одна из версий awk детально не описывается. Последние версии имеют довольно много общих свойств, поэтому документация на любую из них поможет вам изучить и остальные. Мы продемонстрировали вам сценарии для утилиты awk, а также сценарии, которые работают только с утилитами nawk и gawk. Но о свойствах awk упомянуто было лишь вскользь. Подробную информацию обо всех этих утилитах содержит полностью пересмотренное второе издание книги sed & awk издательства O'Reilly & Associates.* Немало соответствующей информации имеется и на компакт-диске. - JP, DD, TOR Мы благодарим Арнольда Роббинса, соавтора второго издания, за помощь, оказанную при подготовке данного параграфа и других материалов об утилите awk. 526 Часть пятая. Редактирование текста
34 Потоковый редактор sed 34.01 Два важных момента, касающихся редактора sed Если вы знакомы с командами глобального редактирования, используемыми, например, в редакторе vi или ех, то уже представляете, какие операции должен выполнять редактор sed. Но есть два момента, которые отличают этот редактор от остальных. 1. Редактор sed не изменяет редактируемый файл. На это как раз и указывает его название Лгеаш editor (потоковый редактор). Он лишь читает стандартный ввод оз.п) или файл, преобразует его и передает на стандартный вывод (U.oi). Если вы хотите получить возможность редактировать файл, необходимо написать "оболочку" (34.03) — сценарий интерпретатора shell, который будет перехватывать стандартный вывод и записывать его в первоначальный файл. 2. . Команды редактора sed по умолчанию являются глобальными. В редакторе наподобие ех команда s/old/new заменит слово old словом new только в текущей строке, если не будут использованы глобальная команда или символы адресации. В редакторе sed все как раз наоборот. Команда, подобная приведенной выше, будет применена ко всем строкам файла. Для ограничения пределов, в которых ищется соответствие шаблону, придется использовать символы адресации. (Однако, как и в редакторе ех, только первое вхождение шаблона в данную строку будет изменено, если в конце команды замены не будет задан флаг g.) Если вам нужно было лишь произвести простую замену, можете приступать к работе. Но если же имеется необходимость в более сложных операциях, воспользуйтесь уникальными и мощными командами, предоставляемыми в ваше распоряжение редактором sed. Мы вовсе не пытались изложить в этой главе все, что касается редактора sed. Краткий справочник команд, а также многочисленные примеры приведены в параграфе 34.24. Поскольку в книге используется масса сценариев редактора sed, нам нужно лишь заложить базу, чтобы начинающие пользователи могли их понимать. Для более опытных пользователей приводятся советы и пространные объяснения относительно того, как использовать некоторые более трудные команды данного редактора. - TOR 34.02 Вызов редактора sed При необходимости использовать редактор sed "на лету", как потоковый редактор (M.ei), запустить его можно вот такой простой командой: % команда | sed 's/old/new' | друтая_коызида Если будет задано имя файла, редактор sed прочитает его вместо стандартного ввода: % sed 's/old/new' myfile Потоковый редактор sed 527
моз Простой сценарий можно запустить прямо в командной строке. Если необходимо выполнить более одной команды редактирования, можно воспользоваться опцией -е: % sed -e 's/old/new' -e '/bad/d' myfile или точкой с запятой (;), которая служит разделителем команд: % sed 's/old/new/; /bad/d' myfile Кроме того, и это касается в первую очередь сценария интерпретатора shell (i.os), можно воспользоваться способностью интерпретатора Bourne shell "понимать" команды, заданные в нескольких строках: sed ' s/old/new/ /bad/d' myfile Наконец, команды можно поместить в файл и с помощью опции -/дать редактору sed указание прочитать инструкции из этого файла: % sed -£ scriptfile myfile Остается еще одна опция командной строки, а именно -я. Редактор sed обычно выводит все строки (за исключением тех, удаление которых задает сценарий редактирования). Но иногда бывает необходимым вывести только те строки, которые сценарий изменяет, или те, которые явно указываются командой р. В таком случае используется опция -я, способная подавить обычный вывод. - TOR 34.03 Тестирование сценариев: программы checksed и runsed Большинство сценариев редактора sed, кроме разве что самых простых, вызываются из "оболочки" — сценария интерпретатора shell (44.01), который активизирует редактор sed и к тому же содержит команды редактирования. Использование "оболочки" является простым способом преобразования сложной командной строки в команду, состоящую из одного слова. Тот факт, что задействуется именно редактор sed, может быть не известен пользователям команды. Ниже описываются два сценария, которыми вы обязательно должны пополнить свой набор инструментальных средств. Они оба используют цикл for (u.u) интерпретатора shell, чтобы иметь возможность применять одни и те же команды редактирования к любому числу файлов. Первый сценарий отображает информацию о вносимых изменениях, так что можно быть уверенным, что команды редактирования выполнены корректно. Второй сценарий сразу вносит изменения в редактируемый файл. Сценарий checksed Сценарий checksed автоматизирует процесс проверки операций редактирования, выполняемых редактором sed. Он ищет в текущем каталоге файл сценария sedscr и применяет его команды к входным файлам, заданным в командной строке. Результаты обработки выводятся программой постраничного вывода; по умолчанию используется программа more. #! /bin/sh script=sedscr for file do echo *********** < = $file > - sed output *********** 13.13 sed -f $script "$file" | diff "$file" - done I $(PAGER-more} Приведем пример тестирования сценария. 528 Часть пятая. Редактирование текста
34.04 $ cat sedscr s/jerry@ora\.com/jpeek@jpeek.com/g $ checksed home.html new.html ********* < = home.html > = sed output ********* 102cl02 < <a href="mailto:jerry@ora.com">Email it</A> or use this form: > <a href="mailto:jpeek@jpeek.com">Email it</A> or use this form: 124Ы24 < Page created by: <a href="mailto:jerry@ora.com">jerry@ora.com</a> > Page created by: <a href="mailto: jpeekSjpeek.com">jpeek@jpeek.com </a> ********* < = new.html > = sed output ********* 22c22 < <a href="mailto:jerry@ora.com">Send comments</A> to me! > <a href="mailto:jpeek@jpeek.com">Send comments</A> to me! Если вы обнаружили, что ваш сценарий редактирования выполнил не те операции, которые вам нужны, измените его и запустите checksed еще раз. Сценарий runsed Сценарий runsed вносит изменения в сам файл. Он применяет пользовательский сценарий sedscr по отношению к входному файлу, создает временный файл, а затем копирует его на место первоначального. К тому же сценарий runsed соблюдает некоторые меры предосторожности при редактировании файлов: • во-первых, он не редактирует файл сценария редактора sed, если в командной строке случайно указан файл sedscr; • во-вторых, вьщает предупреждение при попытке пользователя отредактировать пустой файл или какой-либо другой объект, не являющийся файлом (например, каталог); • в-третьих, не записывает вместо первоначального файла пустой файл, если сценарий редактора sed не порождает никакого вывода (он просто прекращает работу). Сценарий runsed модифицирует файл только в том случае, если пользовательский сценарий sedscr выполнил редактирование. Поэтому временная метка (i6.ns) для файла не будет меняться, если содержимое файла осталось прежним. Подобно сценарию checksed, сценарий runsed надеется найти сценарий под названием sedscr в текущем каталоге. (В параграфе 4.03 рассказано, как можно хранить несколько сценариев редактора sed) Имя или имена файлов, которые следует отредактировать, задаются в командной строке. Для указания ряда файлов используются метасимволы интерпретатора shell (is.oiy. $ runsed *.html runsed: editing home.html: runsed: done with home.html runsed: editing new.html: runsed: done with new.html runsed: all done Сценарий runsed не защитит вас от некорректных сценариев редактирования. Использование сценария checksed позволит проверять все вносимые изменения до того, как runsed сделает их постоянными. (Модифицировав сценарий runsed, можно делать резервные копии исходных версий файлов.) -DD, JP, TOR 34.04 Основы адресации В команде редактора sed можно указать один или два адреса либо не указывать такового вообще. Адрес может быть номером строки, символом $ или регулярным выражением (26.04), определяющим шаблон. Потоковый редактор sed 529 н runsed
34.04 ^ '^J? • Если адрес не указан, команда применяется ко всем строкам файла. • Если указан только один адрес, команда применяется к каждой строке файла, соответствующей адресу. • Если указаны два адреса, разделенные запятой, команда выполняется над всеми строками заданного диапазона. Этот диапазон может встречаться во входных данных несколько раз. • Если за адресом следует восклицательный знак (!), команда применяется ко всем строкам, не соответствующим адресу. Чтобы понять, как выполняется адресация, рассмотрим пример, в котором используется команда удаления d. Сценарий, состоящий только из команды d и не содержащий адресов, d удаляет все строки. Когда в качестве адреса указывается номер строки, команда воздействует только на эту строку. Например, следующий сценарий удаляет только первую строку: Id Номер строки определяется внутренним счетчиком строк, который ведет редактор sed. Этот счетчик при наличии нескольких входных файлов не переустанавливается. Таким образом, сколько бы файлов не указывалось в качестве входных, во входном потоке будет только одна строка с номером 1. Входной поток, аналогичным образом, будет рассматривать только одну строку в качестве последней. Она может быть указана с помощью адресующего символа $. В следующем примере во входном потоке удаляется последняя строка: $d Этот символ $ не следует путать с символом $, используемым в регулярных выражениях, где он обозначает конец строки. Когда в качестве адреса указывается регулярное выражение, команда воздействует только на строки, соответствующие шаблону, который определяется этим выражением. Регулярное выражение должно быть заключено между символами косой черты. Команда удаления /A$/d уничтожает только пустые строки. Все другие строки пропускаются. При указании двух адресов определяется диапазон строк, по отношению к которым выполняется команда. В следующем примере показано, как удалить все строки, находящиеся между двумя макросами, в данном случае между макросами утилиты tbl (43.15) . TS и . ТЕ: /"\.TS/,/A\.TE/d Эта команда удаляет все строки, начиная с соответствующей первому шаблону и заканчивая строкой, соответствующей второму шаблону. Команда не воздействует на строки, не принадлежащие указанному диапазону. Если имеется несколько таблиц (кроме первой 4£ары макросов .TS и .ТЕ существуют и другие такие же пары), эти таблицы тоже будут удалены. Следующая команда удаляет все строки файла, начиная с пятидесятой и заканчивая последней: 50, $d Для задания адреса одновременно можно использовать как номера строк, так и шаблоны: l,/A$/d В данном примере удаляются все строки, начиная с первой и заканчивая пустой строкой. Этим методом можно воспользоваться для удаления, скажем, заголовка сохраненного в файле сообщения электронной почты (из). Первый адрес следует рассматривать как разрешение выполнять действие, а второй — как отмену этого разрешения. В редакторе sed нет средств для предварительного просмотра с 530 Часть пятая. Редактирование текста
34.05 целью определения, будет ли найдено соответствие второму шаблону. Команда станет применяться к строкам, как только будет найдено соответствие первому шаблону. Она будет применяться и ко всем последующим строкам, пока не будет найдено соответствие второму шаблону. В предыдущем примере, если редактируемый файл не содержит пустой строки, то удаляются все строки. Восклицательный знак, который следует после адреса, меняет смысл операции соответствия шаблону на противоположный. Так, с помощью приведенного ниже сценария удаляются все строки, за исключением тех, которые будут обрабатываться утилитой tbl: /*\.TS/,/*\.TE/!d Этот сценарий выделяет из исходного файла входные данные для утилиты tbl. (Такая возможность может быть удобной, в частности, при тестировании формата таблицы.) Фигурные скобки ({}) позволяют задавать более одной команды с использованием одного адреса. Например, приведенная ниже команда находит все строки таблицы, потом среди них ищет строки со словом Caution и во всех словах Caution в этих строках заменяет строчные буквы прописными. Кроме того, она удаляет все строки таблицы, содержащие шаблон . sp 2p: /*\.TS/,/n.TE/{ s/Caution /CAUTION /g /A\.sp 2p/d ) — DD, из книги sed & awk издательства O'Reilly & Associates 34.05 Порядок команд в сценарии Объединение в одном сценарии последовательности команд может привести к неожиданным результатам. Можно и не догадываться о том влиянии, которое одна операция редактирования оказывает на другую. Новички обычно считают, что редактор sed ко всем строкам входных данных сначала применяет одну команду редактирования, а только потом следующую. Но все происходит как раз наоборот. Редактор sed применяет все команды редактирования к первой входной строке и только после этого читает вторую входную строку и применяет к ней сценарий редактирования. Поскольку редактор sed всегда работает с последней версией редактируемой строки, то текущая строка будет изменяться после каждой операции редактирования. Это означает, что шаблон, который мог соответствовать первоначальной входной строке, после выполненной операции редактирования может ей больше не соответствовать. Рассмотрим пример, в котором используется команда замены. Предположим, у нас имеется сценарий для замены слова pig словом cow и слова cow словом horse: s/pig/cow/ s/cow/ horse/ Первая команда, как и полагается, заменит слово pig словом cow. Однако когда вторая команда станет заменять слово cow словом horse, она заменит и слово cow, которое раньше было словом pig. Таким образом, если входной файл содержал слова pig и cow, то в выходном файле будет употребляться только слово horse! Возможность возникновения подобной ошибки обусловлена порядком следования команд в сценарии. Его изменение таким образом, чтобы сначала слово cow заменялось словом horse, а затем слово pig заменялось словом cow, решит проблему. Некоторые команды редактора sed изменяют последовательность обработки строк входного файла. Например, команда N (m.is) загружает другую строку файла в буфер редактирования (называемый областью шаблона), не удаляя текущую строку, поэтому на соответствие шаблонам можно проверить несколько строк. Другие команды дают указание редактору sed завершиться до достижения конца сценария или перейти на команду с меткой. Редактор sed Потоковый редактор sed 531
34.06 поддерживает также второй временный буфер, который называется областью хранения. Содержимое области шаблона можно скопировать в область хранения и позже получить его оттуда. Команды, которые используют область хранения, обсуждаются, в частности, в параграфе 34.13. — DD, из книги sed & awk издательства O'Reilly & Associates 34.06 Пошаговое выполнение команд Я давно заметил, что успех выполнения какой-либо задачи с помощью редактора sed напрямую зависит от того, насколько четко я себе представляю, какие именно операции при этом необходимо произвести. Начиная программировать, я пишу сценарий, состоящий из одной команды, которая выполняет только одно действие. После проверки работы этого сценария добавляю вторую команду, а затем еще одну. И так до тех пор, пока не получится все, чего необходимо было добиться. Мой предварительный список команд не всегда бывает полным, поэтому в процессе отладки и тестирования в него часто приходится добавлять другие пункты. Описанный процесс может показаться скучным. И действительно, бывают случаи, когда весь сценарий пишется "за один присест", а затем производится лишь его тестирование. Однако мы убедительно советуем новичкам действовать пошагово, поскольку при таком подходе легко увидеть, какая из команд выполняется правильно, а какая нет. Если же вы попытаетесь выполнить несколько команд одновременно и у вас возникнут проблемы, то вы увидите, что придете к тому же самому методу, только с другого конца: т.е. вы будете отбрасывать команды одна за другой, пока не установите, в чем причина. — DD, из книги sed & awk издательства O'Reilly & Associates 34.07 Выбор разделителей для регулярного выражения Как в редакторе sed, так и в редакторе vi в команде замены для отделения шаблона поиска от строки замещения используется разделитель. Разделителем может служить любой символ, кроме символов пробела и признака новой строки (редактор vi в этом отношении кажется более строгим, чем редактор sed), хотя обычно в качестве такового используется косая черта (/) (например, s /шзблон_ поиска/ строка_замещения/). Если косую черту содержат шаблон поиска или строка замещения, проще изменить разделитель, а не пробовать избавиться от косой черты. Таким образом, при создании шаблона для путевых имен UNIX, в которых имеются символы косой черты, за разделитель можно взять другой символ, например двоеточие: s:/usr/mail:/usr2/mail: Обратите внимание, что в команде разделитель встречается трижды и обязательно следует после строки замещения. Независимо от того, какой символ выбран в качестве разделителя, для отмены его специального назначения в регулярном выражении используется символ обратной косой черты (\). Если вы не знаете, какие символы могут быть в шаблоне поиска (например, в программе интерпретатора shell, которая имеет дело с входными данными различных типов), наиболее безопасным будет выбрать в качестве разделителя управляющий символ. В параграфе 45.35 рассказано, каким образом управляющий символ задается в качестве разделителя и как он используется в сценариях интерпретатора shell без запоминания в файле. Для указания адреса с помощью шаблона можно использовать любой разделитель. Перед первым разделителем обязательно должна быть набрана обратная косая черта. Так, для удаления всех строк, содержаших подстроку /usr/mail, в качестве разделителя можно применить двоеточие (:): \:/usr/mail:d — DD, из книги sed & awk издательства O'Reilly & Associates 532 Часть пятая. Редактирование текста
34.09 34.08 Символы новой строки в строках замещения В команде замены редактора sed символ обратной косой черты (\) в строке замещения используется обычно для защиты метасимволов. Но он может быть применен и для включения в строку замещения символа новой строки. В следующей входной строке, где все элементы отделены знаками табуляции, Columnl Column2 Column3 Column4 символом новой строки можно заменить второй знак табуляции, s/|tab1/\ 2 34.11 /2 Обратите внимание, что после обратной косой черты в строке не должно быть никаких пробелов. Этот сценарий дает такой результат: Columnl Column2 Column3 Column4 Другой пример демонстрирует преобразование файла из формата для программы trojf во входной формат пакета Ventura Publisher. Он преобразует строку .Ah "Major Heading" предназначенную для программы trojf, в аналогичную строку для пакета Ventura: @А HEAD = Major Heading Особенностью здесь является то, что перед обрабатываемой строкой и после нее должно быть по одной пустой строке. Этот пример иллюстрирует случай, когда замещающий параметр состоит из нескольких строк: /A\.Ah/< s/.\.Ah V\ \ @А HEAD = / s/"//g s/$/\ / ) Первая команда замены замещает .Ah двумя символами новой строки и подстрокой @А HEAD = . Символы обратной косой черты в конце строк необходимы для защиты символов новой строки. Вторая команда замены удаляет кавычки. Последняя команда находит конец строки и добавляет после него символ новой строки. — DD, из книги sed & awk издательства O'Reilly & Associates 34.09 Ссылка на строку поиска в строке замещения Амперсанд (&) в качестве метасимвола используется для представления той подстроки, которой соответствует шаблон (но не всей строки, где найдено соответствие). В частности, его можно использовать при необходимости указать соответствующее слово и окружить его командами программы trojf. В нашем примере слово окружается командами типа s: s/UNIX/\\s-2i\\sO/g Поскольку символы обратной косой черты также являются символами строки замещения, необходимо набрать два символа обратной косой черты, с тем чтобы после обработки интерпретатором остался только один такой символ. Символ & в строке замещения указывает на слово UNIX. Если на входе есть строка on the UNIX Operating System. Потоковый редактор sed 533
34.10 то после команды замены получим следующее: on the \s-2UNIX\sO Operating System. Амперсанд особенно полезен в тех случаях, когда шаблоны с регулярными выражениями соответствуют различным вариантам слов. Он позволяет указывать переменную строку замещения, которая соответствует тому, что в данный момент соответствует шаблону. Предположим, нам нужно заключить в круглые скобки все перекрестные ссылки на нумерованные параграфы документа. Другими словами, любые ссылки наподобие См. параграф 1.4 или См. параграф 12.9 должны появиться в круглых скобках в виде (См. параграф 12 . 9). Регулярное выражение может соответствовать различным сочетаниям цифр. Поэтому мы воспользуемся амперсандом в строке замещения и возьмем в скобки то, чему будет найдено соответствие: s/См. параграф [1-9][0-9)*\.[1-9][0-9]*/(S)/ Амперсанд позволяет ссылаться в строке замещения на все, что было поставлено в соответствие шаблону поиска. В следующем примере перед амперсандом (который является частью строки замещения) ставится символ обратной косой черты: s/ORA/O'Reilly \S Associates, Inc./g Можно упустить из виду, что амперсанд сам входит в строку замещения. Если бы мы не предварили его появление символом обратной косой черты, то в качестве результата получили бы следующее: O'Reilly ORA Associates, Inc.. — DD, из книги sed & awk издательства O'Reilly & Associates 34.10 Ссылка на отдельные части строки поиска В команде замены с помощью специальных символов можно сослаться на любую часть найденной строки и воспроизвести ее в строке замещения. В редакторе sed для выделения и запоминания любой части регулярного выражения используется пара скобок. В одной строке может быть запомнено до девяти таких фрагментов. Для восстановления части строки, соответствующей шаблону, используется оператор \л, где л — это числа из диапазона от 1 до 9, указывающие на отдельные "сохраненные" части строки. (За дополнительной информацией обращайтесь к параграфу 26.04.) Так, чтобы выделить номера параграфов в перекрестных ссылках, необходимо задать такую команду: s/\(Cm. параграф \)\([1-9][0-9]*\.[1-9][0-9]*\)/\l\\fB\2\\fP/ Здесь используются две пары скобок. В первую пару заключена подстрока "См. параграф" (поскольку она не меняется и просто вставляется в строку замещения), во вторую — номер параграфа. В строке замещения первая подстрока восстанавливается оператором \1, вторая — оператором \2, перед которым и после которого указываются команды установки полужирного шрифта (например, См. параграф \fB12.9\fP). Аналогичным образом можно запомнить отдельные части строк и поменять их местами. Предположим, что две части строки разделены двоеточием. Мы можем их запомнить, заключив в скобки, а затем переставить эти части местами в строке замещения: % cat testl first:second one:two % sed 's/\(.*\):\(.*\)/\2:\l' test! second:first two:one Очень важным моментом является то, что подстроки могут быть восстановлены в любом порядке и какое угодно количество раз. — DD, из книги sed & awk издательства O'Reilly & Associates 534 Часть пятая. Редактирование текста
34.13 34.11 Поиск и замещение: одно соответствие шаблону из нескольких возможных Одной из самых необычных опций команды замены редактора sed считается числовой флаг, который позволяет указывать на какое-либо одно из нескольких возможных соответствий шаблону. Применяется такая возможность в тех случаях, когда подстрока, соответствующая шаблону, повторяется в строке несколько раз, а замещение должно быть сделано только в одном случае. Например, в строке, содержащей входные данные для утилиты tbl, есть три знака табуляции, а нам необходимо заменить символом > только второй из них. Такую задачу выполнит следующая команда замены: s/irasi/>/2 |ТАВ| — это символ табуляции, который на экране обычно не видим. Если на вход подается файл, состоящий из одной строки, Column HTAaColumn2|fAgColumn3(TABColumn4 то в результате выполнения приведенного сценария будет получена такая строка: Column l[TABlColumn2>Column3ITABIColumn4 Обратите внимание, что в отсутствие числового флага команда замены сделала бы подстановку только для первого знака табуляции. (Следовательно, числовым флагом по умолчанию можно считать число 1.) Флаг может принимать значения из диапазона от 1 до 512. — DD, из книги sed & awk издательства O'Reilly & Associates 34:12 Преобразование текста Команда преобразования у предназначена для замены в строке строчных букв прописными. На самом деле она выполняет функцию, аналогичную выполняемой командой tr (3s.ii), т.е. вместо каждого символа, имеющегося в первой строке, подставляет эквивалентный символ, найденный во второй строке. Команда y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ преобразует все строчные буквы в соответствующие прописные. Команда y/abcdefghijklmnopqrstuvwxyz/nopqrstuvwxyzabcdefghijklm/ выполнит преобразование типа rotl3. Это простой метод шифрования, при котором каждый алфавитный символ заменяется символом, находящимся в алфавите на 13 позиций дальше, чем исходный. (Шифрование по алгоритму rotl3 часто применяется с целью запретить читать электронные сообщения а.зз> непристойного характера тем, для кого они не предназначены. Возможность автоматического шифрования и дешифрирования поддерживается большинством программ чтения почтовых сообщений, и вы наверняка будете удивлены, узнав, насколько простой алгоритм лежит в основе данной операции. Между прочим, приведенная выше команда способна преобразовывать только строчные буквы; если бы мы указали в тексте еще и прописные буквы, строка не поместилась бы на странице!) - TOR 34.13 Область хранения: временный буфер Область шаблона — это буфер (5з.оо, в котором находится текущая входная строка. Есть еще буфер для временного хранения, который называется областью хранения. Содержимое области шаблона может быть скопировано в область хранения, а содержимое области хранения может быть скопировано в область шаблона. Перемещение данных между этими областями осуществляется посредством группы команд. Область хранения используется как память для временного запоминания информации, и таковой она есть на самом деле. Отдельные команды не могут обращаться к области хранения или изменять ее содержимое. Потоковый редактор sed 535
34.13 Наиболее часто область хранения используется для запоминания копии текущей входной строки на время, пока выполняются какие-то изменения исходной строки в области шаблона. [Область хранения также активно задействуется при выполнении команд пересылки и копирования, которые имеются в большинстве редакторов, но в редакторе sed напрямую не применяются, поскольку последний разработан для построчного редактирования потока текстовых входных данных. — GU\ Перечисленные далее команды воздействуют на область хранения. Команда Hold Get Exchange Обозначение h или н g или G X Действие Скопировать или добавить содержимое области шаблона в область хранения Скопировать или добавить содержимое области хранения в область шаблона Поменять местами содержимое области хранения и области шаблона Каждая из этих команд может иметь адрес, указывающий на одну или несколько строк. Команды Hold (h и Н) перемещают данные в область хранения, а команды Get (g и G) — из области хранения назад в область шаблона. Различие между версиями одной и той же команды, которые начинаются строчной и прописной буквами, состоит в том, что первая перезаписывает содержимое буфера назначения, а вторая добавляет данные к содержимому буфера назначения. Команда hold замещает содержимое области хранения содержимым области шаблона. Команда get замещает содержимое области шаблона содержимым области хранения. Команда Hold помещает после содержимого области хранения символ новой строки, а затем содержимое области шаблона. (Символ новой строки добавляется в область хранения, даже если она пустая.) Команда Get помещает после содержимого области шаблона символ новой строки, а затем содержимое области хранения. Команда Exchange (x) меняет местами содержимое двух буферов. Никакие другие действия с буферами при этом не выполняются. В этом параграфе приводится пример, иллюстрирующий принцип помещения строки в область хранения и последующего ее извлечения оттуда. Мы напишем сценарий для чтения отдельного HTML-файла и копирования всех его заголовков в конец этого файла с целью создания резюме. Заголовки будут начинаться тегами <Н1> или <Н2>: <BODY> <Hl>Introduction</Hl> The blah blah blah <Hl>Background of the Project</Hl> <H2>The First Year</H2> <H2>The Second Year</H2> </BODY> Нам необходимо скопировать эти заголовки в область хранения в то время, когда редактор sed читает их. Когда редактор находит конец документа (по тегу </BODY>), он сначала выводит строку Summary:, а затем — сохраненные заголовки без тегов (<Н1> или <Н2>). Рассмотрим такой сценарий: /Л<Н[12]>/Н /a<\/body>/ < i\ <STRONG>Summary:</STRONG> x G s/<\/*H[12]>//g ) 536 Часть пятая. Редактирование текста
34.14 Все строки, соответствующие тегу <Н1> или <Н2>, добавляются в область хранения. (В это же время все такие строки (за исключением строк, которые подлежат удалению) по умолчанию выводятся в редактор sed.) В последней части сценария отслеживается тег </BODY>. Когда редактор sed находит его, то вставляет заголовок Summary:. Затем в сценарии по команде х происходит замена содержимого области шаблона (где находится тег </BODY>) записанными в области хранения заголовками. Далее по команде G тег </BODY> добавляется в конец заголовков в области шаблона. Команда замены удаляет теги <Н1>, </Н1>, <Н2> и </Н2>. Наконец, по умолчанию выводится область шаблона. Последовательность команд, в которой за х идет команда G, позволяет найти соответствующую строку (в данном случае тег </BODY>) и вставить содержимое области хранения перед строкой, соответствующей шаблону. Это похоже на применение команды i, которая вставляет область хранения в текущую строку. Сценарий может привести файл в порядок и выполнить форматирование. В частности, он способен преобразовать сохраненные заголовки в списки, содержащие теги <UL> и <Ы>. Но данный пример демонстрирует в первую очередь работу с областью хранения. Приведем результат выполнения сценария: % sed -f sedecr report.html <BODY> <Hl>Introduction</Hl> The blah blah blah <Hl>Background of the Project</Hl> <H2>The First Year</H2> <H2>The Second Year</H2> <STRONG>Summary:</STRONG> Introduction Background of the Project The First Year The Second Year </BODY> Другие сценарии, использующие область хранения, описаны в параграфах 34.17 и 25.12. Об аллегорической аналогии, которая разъясняет, каким образом применяется область хранения, рассказано в параграфе 34.16. - DD, JP 34.14 Преобразование части строки Команда преобразования у (34.12) воздействует на все содержимое области шаблона. Посимвольное преобразование части строки — это малоприятная, но посильная задача (хотя иногда и запутанная), о чем. свидетельствует следующий наш пример. [Настоящее предназначение этого примера состоит в том, чтобы продемонстрировать принцип использования не команды у, а, скорее, области хранения, предназначенной для выделения и запоминания части строки. — ГОЛ] Работая над книгой, мы обнаружили, что имена операторов не всегда согласованы между собой. Они должны были быть записаны прописными буквами, но в одних из них все буквы строчные, а в других прописными набраны только начальные символы. Преобразование строчных букв в именах операторов в прописные — задача, конечно же, несложная, но у нас всего было около сотни операторов, а писать столько команд замены типа s/find the Match statement/find the MATCH statement/g — работа слишком утомительная. Потоковый редактор sed 537
34.14 Выполнять перекодировку строчных букв в прописные может команда преобразования, но она применяется по отношению ко всей строке. Эта задача становится осуществимой благодаря наличию области хранения, которую можно использовать для запоминания копии входной строки, в то время как имена операторов выделяются и подвергаются перекодировке в области шаблона. Но рассмотрим сначала сценарий: # Перекодировка строчных букв в прописные в именах операторов /the .* statement/i h s/.*the \(.*\) statement.*/\l/ y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ G s/\(.*\)\n\(.*the \).*\( statement.+\)/\2\l\3/ } Заданный адрес ограничивает действие процедуры строками, соответствующими шаблону the .* statement. Теперь поговорим о том, что делает каждая команда. h Команда hold копирует текущую входную строку в область хранения. Используя строку find the Match statement из приведенного выше примера, мы будем показывать, что содержится в области шаблона и области хранения. После выполнения команды h обе эти области имеют одинаковое содержимое. Область шаблона: find the Match statement Область хранения: find the Match statement s/.+the \(.*\) statement.*/\l/ Команда замены выделяет имя оператора из строки и заменяет им целую строку. Область шаблона: Match Область хранения: find the Match statement y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ Команда преобразования заменяет все строчные буквы прописными. Область шаблона: match Область хранения: find the Match statement G Команда Get добавляет строку, которая была сохранена в области хранения, к содержимому области шаблона. Область шаблона: MATCHVnfind the Match statement Область хранения: find the Match statement s/\(..*\)\n\(. *the \).*\( statement. *\)/\2\l\3/ Команда замены находит соответствие для трех частей шаблона: (1) все символы до символа новой строки включительно; (2) все символы после признака новой строки до слова the, включая это слово и пробел; (3) все символы, начинающиеся пробелом и последующим словом statement, до конца области шаблона. Имя оператора, каким оно есть в первоначальной строке, не. сохраняется. В строке замены этой команды восстанавливаются сохраненные части области шаблона, но собираются они в другом порядке: имя команды, содержащее теперь только прописные буквы, помещается между словами the и statement. Область шаблона: find the MATCH statement Область хранения: find the Match statement 538 Часть пятая. Редактирование текста
34.15 Посмотрим, что даст выполнение теста, воспользовавшись проверочным файлом find the Match statement Consult the Get statement. using the Read statement to retrieve data Мы получим следующий результат: find the MATCH statement Consult the GET statement. using the READ statement to retrieve data Как видно из данного сценария, область хранения может быть использована для выделения и манипулирования частями входной строки. — DD, из книги sed & awk издательства O'Reilly & Associates 34.15 Редактирование с переходом через границы строк Большинство программ, использующих регулярные выражения (2б.щ, обладают способностью сравнивать с шаблоном только одну входную строку. По этой причине трудно, например, находить и изменять предложения, так как они могут начинаться где-то в конце одной строки, а заканчиваться в начале следующей. Некоторые шаблоны имеют смысл только при условии, что применяются они к нескольким строкам. Редактор sed может загружать в область шаблона более одной строки. Это позволяет находить (и изменять) символьные последовательности, которые охватывают несколько строк. В данном параграфе показано, как можно создать область шаблона, содержащую несколько строк, и как манипулировать ее содержимым. Команда Next (N, next — следующий) создает многострочную область шаблона, читая новую строку входных данных и добавляя ее к содержимому области шаблона. Прежнее содержимое области шаблона и новая строка разделяются символом новой строки. Символ новой строки в шаблоне может задаваться комбинацией символов \п. В многострочном шаблоне символ А соответствует символу новой строки в начале области шаблона, а $ — концу области шаблона. Рассмотрим первый пример. Предположим, что нам необходимо фразу Owner and Operator Guide заменить фразой Installation Guide, которая, как оказалось, находится в двух строках; причем слова Operator и Guide разделены. Вот как могут выглядеть несколько строк этого текста: Consult Section 3.1 in the Owner and Operator Guide for a description of the tape drives available on your system. В следующем сценарии производится поиск слова Operator в конце строки, читается следующая строка входных данных и выполняется замещение: /Operator$/{ N s/Owner and Operator\nGuide/Installation Guide/ } В данном примере известно, где эти две строки разделяются, т.е. где находится символ новой строки. В случае выполнения на проверочном файле сценарий создает на выходе две строки, в одной из которых объединяется содержимое первой и второй строк. Такая строка слишком длинная, поэтому здесь мы ее не показываем. А длинной она получается потому, что команда замены находит принадлежащий первым строкам символ новой строки, но не возвращает его на место. К сожалению, мы не можем использовать для вставки символа новой строки в строку замещения обозначение \п. Чтобы вернуть символ новой строки на место, необходимо либо применить отменяющую его специальный смысл обратную косую черту, как это делается в команде s/Owner and Operator\nGuide /Installation Guide\ / Потоковый редактор sed 539
34.15 либо использовать операторы \ (,..\) (З4.юу. s/Owner and Operator\ (\n\) Guide /Installation GuideM/ Данная команда восстанавливает символ новой строки после фразы Installation Guide. Она также необходима для удаления пробела, который следует после слова Guide, — новая строка не должна начинаться с пробела. Результат выполнения нашего сценария представлен ниже: Consult Section 3.1 in the "Installation Guide for a description of the tape drives available on your system. He забывайте, что символ новой строки не следует удалять. Если же это сделать, то некоторые строки станут слишком длинными. А что если в файле выражение Owner and Operator Guide встречается еще несколько раз? В таком случае можно изменить адрес и использовать для сравнения слово Owner — первое слово в шаблоне (а не последнее), а затем модифицировать регулярное выражение таким образом, чтобы осуществлялся поиск пробела или символа новой строки между словами: /Owner/{ N s/Owner *\n*and *\n*Operator *\n*Guide/Installation Guide/ } Звездочка (*) указывает на то, что пробел или символ новой строки являются необязательными. Однако такой подход лишь усложняет выражение. Есть более общий метод решения задачи: мы можем прочитать новую строку в область шаблона, а затем использовать команду замены, чтобы удалить принадлежащий строке символ новой строки, где бы он ни находился: s/Owner and Operator Guide/Installation Guide/ /Owner/( N s/ *W / s/Owner and Operator Guide */Installation Guide\ / } Первая строка сценария соответствует шаблону Owner and Operator Guide при условии, что он содержится в одной строке. (Ответ на вопрос, почему это необходимо, вы получите в конце параграфа.) Если находится соответствие слову Owner, то следующая строка читается в область шаблона, а принадлежащий ей символ новой строки заменяется пробелом. Затем мы пытаемся найти соответствие для всего шаблона и выполняем замену, добавляя в конце символ новой строки. Этот сценарий будет выполнять сравнение с шаблоном Owner and operator Guide независимо от того, как слова размещены в отдельных строках. Приведем текстовый файл для проверки расширенного сценария: Consult Section 3.1 in the Owner and Operator Guide for a description of the tape drives available on your system. Look in the Owner and Operator Guide shipped with your system. Two manuals are provided, including the Owner and Operator Guide and the User Guide. The Owner and Operator Guide is shipped with your system. Выполнение приведенного выше сценария на проверочном файле дает следующий результат: % sed -f sedscr sample Consult Section 3.1 in the Installation Guide 540 Часть пятая. Редактирование текста
34.16 for a description of the tape drives available on your system. Look in the Installation Guide shipped with your system. Two manuals are provided, including the Installation Guide and the User Guide. The Installation Guide is shipped with your system. Может показаться, что в сценарии одна команда замены является лишней. Первая команда выполняет сравнение с шаблоном, когда тот полностью помещается в одной строке, а вторая — когда в область шаблона заносятся две строки. Чтобы понять, зачем нужна первая команда, удалите ее из сценария и выполните этот сценарий на том же проверочном файле: % sad -f sadacr2 samplo Consult Section 3.1 in the Installation Guide for a description of the tape drives available on your system. Look in the Installation Guide shipped with your system. Two manuals are provided, including the Installation Guide and the User Guide. Обратили ли вы внимание на две сложные ситуации, которые здесь возникают? Наиболее очевидным является то, что последняя строка не вывелась. В последней строке происходит сравнение со словом Owner, а затем, когда уже нечего вводить, выполняется команда N. Поэтому редактор sed завершает работу и не выводит даже этой строки. Для большей уверенности команду N нужно было бы применить следующим образом: $!N В этом случае для последней строки ($) команда N не выполнялась бы. Сравнивая последнюю строку с шаблоном Owner and Operator Guide в нашем сценарии, мы избежим ее сравнения с шаблоном Owner и не станем применять команду N. Однако если слово Owner появится в последней строке, мы будем иметь ту же ситуацию, как если бы команда $! N не применялась. Вторая проблема менее заметна. Она связана с выражением Owner and Operator Guide во втором абзаце. Во входном файле данное выражение занимало отдельную строку: Look in the Owner and Operator Guide shipped with your system. В полученном результате, который приведен выше, пустая строка после слов shipped with your system отсутствует. Объясняется это тем, что данная строка сравнивается с шаблоном Owner и со следующей пустой строкой, добавленной в область шаблона. Команда замены удаляет символ новой строки, который находится в области шаблона, и пустая строка, таким образом, исчезает. (Если бы строка не была пустой, символ новой строки все равно был бы удален и текст появился бы на той же строке, где находится фраза shipped with your system.) Наилучшее решение, похоже, состоит в том, чтобы не читать следующую строку, если для шаблона можно найти соответствие в одной строке. Именно по этой причине в первой команде делается попытка сравнения для случая, когда заданный шаблон целиком находится в одной строке. — DD, из книги sed & awk издательства O'Reilly & Associates 34.16 Осмотрительный писарь Действия наиболее трудных для понимания команд редактора sed, предназначенных для копирования и добавления данных в область шаблона и область хранения, — команд h, H, g, G и х — можно сравнить с действиями крайне осмотрительного средневекового писаря, Потоковый редактор serf 541
34.16 занимающегося переписыванием рукописи. Его работа сопровождается некоторыми пространственными ограничениями: оригинал рукописи доступен для обозрения в одной комнате, инструкции по переписыванию рукописи хранятся в другой комнате, а перья, чернила и писчий материал находятся в третьей. Оригинал рукописи, а также инструкции относительно ее переписывания вытесаны на камне, и их невозможно переместить. Исполнительный писарь, повинующийся человеческому голосу более, нежели голосу разума, может сделать копию рукописи, бегая из одной комнаты в другую и переписывая только по одной строке. Войдя в комнату, где находится оригинал рукописи, он извлекает из кармана лист бумаги, чтобы переписать на него первую строку рукописи. Затем он направляется в комнату, где находятся инструкции, касающиеся переписывания. Он читает каждую инструкцию, чтобы посмотреть, применима ли она к той единственной строке, которую он переписал. Каждая инструкция, записанная специальными знаками, состоит из двух частей: шаблона и процедуры. Писарь читает первую инструкцию и проверяет шаблон на соответствие его строке. Если соответствие отсутствует, писарь не беспокоится о процедуре и рассматривает следующую инструкцию. Если соответствие обнаруживается, он выполняет действие или действия, которые указаны в процедуре. Прежде чем попытаться сравнить строку с шаблоном из следующей инструкции, он осуществляет редактирование на своем листе бумаги. Не забывайте, что писарь 'должен прочитать всю последовательность инструкций, а не только первую, в которой найдено соответствие шаблону, и он действительно это делает. Поскольку он выполняет редактирование в процессе чтения инструкции, то всегда пытается сравнить со следующим шаблоном последнюю версию строки; содержание первоначальной строки он не всегда помнит. Добравшись до конца списка инструкций и выполнив на своем листе бумаги все необходимое редактирование, писарь идет в следующую комнату, чтобы записать отредактированную строку. (Ему не надо давать указаний типа: "Эту строку следует записать на бумагу".) После того как все это будет сделано, писарь возвратится в первую комнату и перепишет на новый лист бумаги следующую строку. А перейдя во вторую комнату, он снова перечитает все инструкции. Вот так он обычно поступает, если не оговорен другой принцип работы. Например, перед началом работы ему могут велеть не записывать все отредактированные строки (опция -и). В этом случае он должен ждать указания выводить информацию (опция р). Если он не получает такой инструкции, то выбрасывает свои листы бумаги и продолжает действовать дальше "по алгоритму". И независимо от того, сказано ему записывать строки или нет, он выполняет все до последней инструкции. Давайте посмотрим на инструкции другого рода, которые писарю необходимо интерпретировать. Прежде всего следует отметить, что в инструкции могут иметься один или два шаблона, но не исключено, что таковые вообще будут отсутствовать. • Если не указан ни один шаблон, то ко всем строкам применяется одна и та же процедура. • При наличии только одного шаблона процедура будет выполняться для каждой строки, соответствующей этому шаблону. • Если за шаблоном следует восклицательный знак (!), процедура выполняется для всех строк, которые не соответствуют данному шаблону. • В том случае, когда указано два шаблона, описанные в процедуре действия выполняются для первой (соответствующей первому шаблону) и всех последующих строк, пока не встретится строка, соответствующая второму шаблону. Писарь может работать только построчно. А как же он обрабатывает диапазон строк? Каждый раз при просмотре инструкции писарь сравнивает со строками только первый из двух шаблонов. Обнаружив строку, соответствующую первому шаблону, он начинает поочередно сравнивать все строки со вторым шаблоном. Он поступает со вторым шаблоном так, как если бы последний имел обозначение шаблон!, вследствие чего выполнит заданную процедуру только в том случае, если соответствия этому шаблону не существует. Если есть соответствие второму шаблону, писарь снова начинает поиски первого шаблона, но уже в новой строке. Каждая процедура содержит одну или несколько команд, называемых также действиями. Не забывайте, что если шаблон указан вместе с процедурой, то сначала для него выполняется 542 Часть пятая. Редактирование текста
34.17 сравнение и только потом применяется процедура. Мы уже видели много полезных команд, похожих на другие команды редактирования. Однако есть и несколько очень необычных команд. Например, команда N даст писарю указание прямо сейчас пойти, прочитать другую строку и написать ее на том же листе бумаги. Писарю может быть дано указание сохранить строку на отдельном листе бумаги. Команда h "прикажет" ему переписать строку на другой лист и положить его в карман, команда х — заменить лист бумаги из кармана тем, который находится в руках. По команде g писарь выбросит лист, находящийся у него в руках, и заменит его листом из кармана. По команде G он допишет строку со спрятанного листа на лист, который держит перед собой. Получив команду d, он выбросит бумагу вообще и начнет просматривать список инструкций сначала. Команда D означает, что писарь должен переписать на имеющийся лист еще две строки, а затем первую из них вычеркнуть. Если хотите, мы можем эту аналогию снова перенести на мир компьютеров. В таком случае первая и последняя комнаты в нашем средневековом замке станут стандартным входом и стандартным выходом. Следовательно, исходный файл никогда не меняется. Строка на листе бумаги писаря соответствует строке в области шаблона; строка на листе, который он держит в кармане, — это строка в области хранения. Область хранения позволяет сохранять дубликат строки, пока исходная строка изменяется в области шаблона. В следующем параграфе применение алгоритма работы писаря продемонстрировано на примере функционирования программы sed, которая ищет отдельные фразы, располагающиеся в двух строках. - DD 34.17 Поиск шаблонов, расположенных в разных строках [В: параграфе 27.11 рассказывалось о сценарии под названием cgrep.sed — программе широкого назначения наподобие команды grep, созданной с помощью редактора sed. Эта программа позволяет вести поиск одного или нескольких слов, находящихся в ряде строк. В данном параграфе описываются приемы, которые следует использовать при работе с редактором sed для выполнения поиска. Читатель познакомится с той областью программирования, знание которой необходимо для создания развитых прикладных программ этого непонятного, но в то же время замечательного редактора. (Параграфы 34.13—34.16 содержат лишь предварительные сведения, необходимые для понимания настоящего материала.) — JPJ Давайте еще раз рассмотрим два примера из параграфа 27.11. Первая приведенная ниже команда ищет в файле main.c все строки, содержащие слово system, и показывает дополнительно по десять строк контекста над и под каждой строкой, в которой найдено соответствие шаблону. Вторая команда находит случаи вхождения слова awk, за которым следует слово perl, в пределах следующих трех строк: сдгер -10 system main.c сдгер -3 "awk.*perl" Далее следует сценарий редактора sed: #!/bin/sh/ # cgrip - версия команды grep для многострочного контекста, использующая редактор sed # Син/таксис: сдгер [-контекст] шаблон [файл...] п=3 ase44.06 case $1 in -[1-9]*) схрт4!.2в n='expr 1 - "$V shift esac ОД 45.12 re=${l?}; shift Потоковый редактор serf 543
34.17 sed -n " lb start : top \~$re~< h; n; p; H; g; b endif } N : start //{ =; p; } : endif $n, \$D b top "$<S>" 44.IS " "$@" Сценарий редактора sed встроен в "оболочку" (44.14) для выполнения анализа начальных аргументов, поскольку, в отличие от программ awk и perl, указанный редактор не может прямо получить параметры командной строки. Если первый аргумент имеет вид -контекст, то переменной п присваивается значение, на единицу превышающее число заданных строк. При этом используется небольшая уловка: аргумент рассматривается как отрицательное число и вычитается из единицы. После этого шаблон, заданный вторым аргументом, запоминается в переменной $ге, а конструкция ${1?} заставляет интерпретатор shell досрочно завершить работу, выдав сообщение об ошибке, если шаблон не задан. Все оставшиеся аргументы передаются команде sed в качестве имен файлов. Чтобы получить возможность использовать параметры $ге и $п, команды сценария редактора sed следует заключить в двойные кавычки (8.н). Мы применяем опцию -п потому, что не хотим выводить каждую строку, а также потому, что нам нужно использовать в сценарии команду п без побочного эффекта, т.е. без вывода строк. Сам сценарий редактора sed выглядит в какой-то мере неструктурированным (хотя на самом деле он разработан с использованием блок-схемы), но базовый алгоритм достаточно прост, чтобы понять его. В области шаблона мы поддерживаем "окно" из п строк и пропускаем через него входной поток. Если в этом окне обнаруживается совпадение с шаблоном, выводится все содержимое окна (в том числе и п предыдущих строк). Кроме того, выводится каждая последующая строка, пока шаблон не выйдет за границы окна (предоставив, таким образом, п последующих строк). Выражение N; D в редакторе sed используется для прокрутки содержимого окна, причем команда D не станет удалять строки из области контекста, пока не будут прочитаны первые п строк. Ядро этого сценария в своей основе является конструкцией if-then-else, с помощью которой определяется, находимся ли мы в текущий момент в области контекста. (Границы используемого при этом регулярного выражения отмечаются знаком тильды (~), поскольку вероятность того, что этот знак встретится в заданном пользователем Шаблоне, намного меньше, чем вероятность появления здесь символа обратной косой черты.) Если мы все еще находимся в контексте, то следующая строка ввода читается и выводится, а область хранения временно используется для запоминания содержимого окна (при этом выполняется команда N). В противном случае мы добавляем следующую строку (команда N) и снова ищем соответствие заданному шаблону (пустое регулярное выражение означает повторное использование последнего шаблона). Если на этот раз соответствие шаблону найдено, то таковой должен быть отображен. Поэтому мы выводим на экран номер текущей строки, а также содержимое окна. Последующие команды будут выполняться по ветви then до тех пор, пока шаблон не исчезнет из окна. - GU 544 Часть пятая. Редактирование текста
$4.18 34.18 Удаление нескольких строк Команда d редактора sed удаляет содержимое области шаблона (М.Н) и читает новую строку входных данных. При этом редактирование возобновляется с первых команд сценария. Команда D работает несколько иначе: она удаляет часть содержимого области шаблона, до первого символа новой строки включительно. Она, не читая новой строки входных данных, заставляет вернуться в начало сценария и применить все его инструкции к тем строкам, которые остались в области шаблона. Разницу между этими командами можно увидеть, написав сценарий, который будет искать последовательности пустых строк и вместо них вставлять только одну пустую строку. В сценарии, приведенном ниже, используется команда удаления d: # из нескольких пустых строк оставляется одна; # это вариант сценария, в котором используется команда d /Л$/Ч N /Л\п$/с1 } Если встречается пустая строка, в область шаблона добавляется следующая строка. Затем делается попытка сравнить с шаблоном символ новой строки. Обратите внимание, что метасимволы Л и $ соответствуют началу и концу области шаблона. Ниже приводится текстовый файл для проверки описанного сценария. Пооле этой строки следует 1 пустая строка. После этой строки следуют 2 пустые строки. После этой строки следуют 3 пустые строки. После этой строки Следуют 4 пустые строки. Конец. Выполнение сценария на проверочном файле дает такой результат: % sed -f sed.blank test.blank После этой строки следует 1 пустая строка. После этой строки следуют 2 пустые строки. После этой строки следуют 3 пустые строки. После этой строки следуют 4 пустые строки. Конец. При наличии четного количества пустых строк все они удаляются. А если число пустых строк нечетное, одна пустая строка сохраняется. Объясняется это тем, что команда удаления d полностью очистила область шаблона. Когда встречается первая пустая строка, в область шаблона читается следующая строка и обе строки удаляются. Если встречается третья пустая строка, а следующая строка не является пустой, команда удаления не применяется и пустая строка попадает в выходные данные. Воспользовавшись командой D, /*\n$/D мы получим другой результат — именно такой, какой нам нужен. Дело в том, что при наличии двух пустых строк команда D удаляет только первую из них. Затем, при новом выполнении сценария, пустая строка вызовет чтение еще одной строки в область шаблона. Потоковый редактор sed 18 9-171 545
34.19 Если эта строка окажется непустой, то выведены будут обе строки, в том числе и одна пустая. Другими словами, если в области шаблона присутствуют две пустые строки, удаляется только первая. Если за пустой строкой следует текст, область шаблона выводится нормально. — DD, из книги sed & awk издательства O'Reilly & Associates 34.19 Выполнение редактирования в любом месте, кроме... Существует два способа избежать изменений в определенных частях документа, редактируемого с помощью редактора sed. Во-первых, можно использовать команду !, указав, что команды редактирования применяются только к строкам, которые не соответствуют шаблону. Во-вторых, можно применить команду перехода Ь, позволяющую пропускать некоторые части сценария редактирования. Рассмотрим пример. Обратившись к параграфу 43.21, мы узнаем, что редактор sed используется в процессе предварительной обработки входных данных для программы troff и предназначен для автоматического преобразования двух коротких тире (--) в одно длинное (—), а прямых кавычек ("") — в фигурные (""). Однако примеры программ в технической литературе обычно набираются моноширинным шрифтом, что позволяет четко видеть каждый символ таким, каким он появляется на экране компьютера. Во время набора текста документа мы не хотим, чтобы редактор sed применял к примерам программ те же правила редактирования, что и к обычному тексту. В частности, не следует заменять прямые кавычки фигурными. Поскольку примеры программ заключаются в пару макросов (наподобие .ES и .ЕЕ), то последние служат указанием для применения других правил редактирования. Мы можем дать такое указание: /~\.ES/,/A\.EE/!{ s/\\<em'7\\(enrVg ) На все команды, заключенные в фигурные скобки ({}), будет распространяться адрес, заданный начальным шаблоном. Есть и другой способ сделать то же самое. Команда b позволяет передавать управление другой строке сценария, обозначенной необязательной меткой. Используя это свойство, приведенный выше сценарий можно переписать так: /"\.ES/,/A\.EE/bend з/Л,7"/ s/\\(em'7\\(em4 ~/g :end Метка состоит из двоеточия (:), за которым могут следовать не более семи символов. Если метка не указана, команда b передает управление в конец сценария. (В приведенном выше примере метка end вставлена лишь для того, чтобы показать, как вообще используются метки; в данном случае она не является обязательной.) Команда b предназначена для управления последовательностью выполнения команд сценария. Она позволяет создавать "подсценарии", которые будут применяться только к строкам, соответствующим определенным шаблонам. Данная команда, как это характерно и для нашего случая, является и мощным средством исключения части текста из сферы действия одноуровневого сценария. Преимущество команды b по сравнению с командой ! состоит в том, что с ее помощью проще указать несколько условий для изменения правил редактирования. Параметр ! может применяться к одной команде или к последовательности команд, которые заключены в фигурные скобки и следуют сразу после параметра !. С другой стороны, команда b предоставляет пользователю почти неограниченный контроль над ходом выполнения сценария. - TOR 546 Часть пятая. Редактирование текста
34.21 34.20 Команда проверки По команде проверки t (test — проверить) выполняется переход на метку (или в конец сценария), если в строке, задаваемой текущим адресом, замена прошла успешно. По этой команде осуществляется условный переход. Синтаксис команды t следующий: [адрес] t [метка] Если метка не указана, управление передается в конец сценария, если указана — возобновляется в строке, которая следует за меткой. Проанализируем сценарий проверки правописания, предложенный Грегом Уббеном. Сценарий исправляет общие (в данном примере простые) ошибки в правописании. Команда t сообщает о выполненных исправлениях: h s/seperate/separate/g s/compooter/compute r/ s/said editor/sed editor/g s/lable/label/g t changed b : changed P g s/.*/[WAS: &]/ t Команда h (34.13) сохраняет копию текущей входной строки. После этого, если одна из четырех замен происходит успешно, команда t changed выполняет переход на соответствующую метку (: changed) в конце сценария. В противном случае, если ни одна из команд s не дала положительного результата, команда b возобновляет выполнение сценария со следующей строки (и как это всегда бывает в редакторе sed, входная строка выводится до начала выполнения сценария). После перехода на метку сценарий выводит текущую входную строку (строку, содержащую орфографическую ошибку, которая к данному времени уже исправлена). Затем по команде g (34.13) извлекается первоначальная, не исправленная строка, которая по команде s заключается в скобки: [WAS: xxx]. Вот результат работы сценария: $ sad -f sedscr afilo This is a separate test. [WAS: This is a seperate test.] I put a label on my computer! [WAS: I put a lable on my compooter!] that's all for now. Последняя команда t в сценарии используется для того, чтобы обойти ошибки некоторых версий редактора sed. Грег говорит: "Предполагается, что флаг t будет сброшен либо после выполнения команды t, либо после чтения новой строки входных данных. Но отдельные версии редактора sed не сбрасывают его при вводе новой строки. Я добавил эту ничего не делающую команду t, желая быть уверенным, что данный флаг будет сброшен после предыдущей команды sill, которая всегда порождает значение ИСТИНА". Попытайтесь выполнить сценарий без дополнительной команды t. Если вам не удастся этого сделать, значит, ваш редактор sed имеет ошибку и вам следует приобрести новую версию редактора, например GNU gsed. - JP, DD 34.21 Команда выхода Команда выхода q заставляет редактор sed прекращать чтение и вывод новых входных строк. Она имеет такой синтаксис: [ адрес_строки] q Потоковый редактор serf 18* 547
34.22 SN£ Эта команда может иметь только адрес одной строки. Когда будет прочитана строка, Л"4 соответствующая указанному адресу (адрес строки), выполнение сценария прекратится. Так, в следующем сценарии, состоящем из одной строки, команда q используется для вывода [34.22] первых десяти строк файла: % sad 'Wq' myfilo Редактор sed выводит все строки, пока не получит десятую строку, а затем завершает работу. Предыдущий вызов редактора sed более эффективен, чем такой его эквивалент: -пмм % sed -n 'l,10p ' myfilo (особенно если myfile — это длинный файл), поскольку редактору теперь не надо продолжать читать входные данные и проверять их на соответствие ^аблону. Еще один способ применения команды q — это прекращение выполнения сценария, когда из файла прочитано все, что необходимо было прочитать. Было бы нецелесообразно продолжать просматривать большой файл после того, как редактор sed нашел все, что ему было нужно. Именно такой подход используется в сценарии getmac (43.20). - TOR 3422 Опасности, связанные с использованием команды выхода Команда выхода q (34.21) редактора sed весьма полезна в случае необходимости прекратить обработку последующих входных данных после выполнения всех заданных действий. Эту команду нельзя использовать в сценариях редактора sed, которые записывают свои результаты редактирования в исходный файл. После выполнения команды q дальнейший вывод прекращается. Команда ни в коем случае не должна использоваться там, где необходимо отредактировать начало файла и оставить неизменной остальную его часть. Применение команды q в этом случае — ошибка, весьма типичная для новичков. - TOR 34.23 Символы новой строки и обратная косая черта в сценариях интерпретатора shell Ввести символ новой строки при работе с редактором sed несложно; настоящее искусство необходимо, чтобы вставить его при работе с интерпретатором С shell. Чтобы вставить символ новой строки в команду замены, его сначала необходимо защитить посредством обратной косой черты (см. документацию на редактор sed). [Заключите эти команды в одинарные кавычки ('), как это сделал Крис. При использовании двойных кавычек (") сценарий превращается в команду s/foo/bar/, что связано с особенностями обработки символов обратной косой черты и новой строки (S.U). — JP\: sed -e 's/foo/b\ а\ г Г Действительно, интерпретатор Bourne shell будет прекрасно работать, обращаясь со входными данными надлежащим образом (s.u>. Однако интерпретатор С shell, считая себя умнее всех (47.02), удаляет конечные символы, а также символы обратной косой черты (s.is), и вам вместо Предыдущего сценария приходится набирать следующее: sed -e 's/foo/b\\ а\\ г/1 Возможно, лучшим выходом из такой ситуации является помещение команд редактора sed в отдельный файл (34.02), чтобы они лишний раз не привлекали внимания интерпретатора shell. - СТ, из телеконференции net.unix в Usenet, 20 ноября 1985 г. 5Ю Часть пятая. Редактирование текста
34.24 34.24 Краткий справочник по редактору sed Несколько слов о. том, как работает редактор sed. • Каждая строка входных данных копируется в область шаблона. • Команды редактирования (если задается более одной команды, перед каждой из них устанавливается опция -е) и (или) файлы сценария указываются в командной строке после опции -/ Все команды редактирования последовательно применяются к каждой из вводимых строк. • Если адреса строк не заданы, команды редактирования применяются ко всем строкам. • Если какая-либо команда изменила входную строку, то последующая команда будет применяться не к исходной строке, а к текущей строке в области шаблона. • Исходный входной файл остается неизменным; команды редактирования модифицируют копию входной строки. Эта копия передается на стандартный вывод озли), если не была применена опция -и; стандартный вывод может быть перенаправлен в файл озли з4.оз). Синтаксис команд редактора sed Как правило, команды редактора sed задаются в таком виде: [адрес] [, адрес] [!] команда [аргументы] Как видите, они состоят из адресов и команд редактирования. Команды задаются одной буквой или символом (далее они описываются в алфавитном порядке). К аргументам относятся метки, применяемые в командах b и t; имена файлов, используемые в командах г и w; флаги замены, которые указываются в команде s. Адреса также описаны ниже. Элементы, которые при описании синтаксиса команд берутся в квадратные скобки, не являются обязательными; сами скобки при использовании этих элементов набирать не нужно. Фигурные скобки ({}) используются в редакторе .serf при необходимости выполнить операцию вложения одного адреса в другой или в случае применения с одним и тем же адресом нескольких команд: [адрес] [, адрес] ( команда 1 команда 2 } Левая фигурная скобка ({) — это оператор, которым начинается группа команд редактора sed. Заканчивается такая группа правой фигурной скобкой (}). Команды, заданные в фигурных скобках, могут занимать несколько строк, как показано выше; если же они находятся в одной строке, то после каждой команды, в том числе и указанной последней, вводится точка с запятой (;): [адрес] [,адрес] (команда!; . . .командам; } Адресация с помощью шаблонов Команды редактора sed могут содержать один или два адреса либо вообще не содержать такового. Адресом чаще всего служит номер строки, символ $ (для последней строки) или регулярное выражение, окруженное символами косой черты (/шаблон/). (Регулярные выражения описаны в главе 26.) Сочетание \п используется для обозначения символа новой строки в области шаблона (появляется в результате выполнения команды N), но не для обозначения символа новой строки в конце области шаблона (см. параграф 34.04). Способ задания адреса Адрес не указан Указан один адрес Область действия команды Все входные строки Все входные строки, соответствующие адресу. В некоторых командах (a, i, r, q и =) может быть только один адрес Потоковый редактор sed 549
34.24 Способ задания адреса Область действия команды Указаны два адреса, разделенные запятой Указан один адрес, после которого следует символ ! Все строки, начиная со строки, соответствующей первому адресу, и кончая строкой, соответствующей второму адресу. Повторяется для всех диапазонов строк, соответствующих адресам Все строки, не соответствующие адресу Примеры Произвести операцию замены во всех строках (все вхождения): s/xx/yy/g Удалить строки, содержащие подстроку BSD: /BSD/d Вывести все строки, находящиеся между словами BEGIN и END, включая и строки с этими словами: /'-BEGIN/, /AEND/p Удалить все строки, не содержащие слова SAVE: /SAVE/!d Произвести операцию замены во всех строках, за исключением тех строк, которые находятся между словами BEGIN и END: /BEGIN/,/END/!s/xx/yy/g Алфавитный список команд # Начало комментария в сценарии редактора sed. Если первой такой строкой является #п, то редактор sed выполняется с опцией командной строки -я. : '.метка Служит меткой для строки сценария, на которую передается управление по команде b или t. В метке может быть до семи символов. [адрес]= Выводит номер каждой строки, соответствующей адресу. а [адрес] а\ текст После каждой строки, соответствующей адресу, добавляет текст. Если текст занимает более одной строки, все символы новой строки, за исключением последнего, предваряются символами обратной косой черты. Завершать текст будет символ новой строки, который не скрыт таким способом. Текст не доступен в области шаблона, и к нему не могут применяться команды замены. Результат этой команды посылается на стандартный вывод после выполнения всех команд редактирования, независимо от того, что произошло с текущей строкой в области шаблона. (Пример использования этой команды приведен в параграфе 43.22.) Пример $а\ Этот текст добавляется в конец файла\ (последняя строка файла обоэначается\ символом $). В конце каждой строки текста\ (за исключением последней)\ используется знак отмены специального\ Смысла — обратная косая черта. 550 Часть пятая. Редактирование текста
34.24 b [адрес1][,адрес2] Ь [метка] Безусловный переход на метку, расположенную где-либо в сценарии. Это означает, что следующей будет выполняться команда, которая следует после метки. Если метка не указана, управление передается в конец сценария, поэтому команды к текущей строке больше не применяются (см. параграфы 34.19 и 34.17). Пример # Пропустить таблицы утилиты tbl; # возобновить выполнение сценария после макроса ТЕ: /~\.TS/,/A\.TE/b с [адрес1] [,адрес2] с\ текст Заменить строки, определяемые адресом, текстом. Когда указывается диапазон строк, все строки как единая группа заменяются одной копией текста. В каждой строке текста, за исключением последней, символам новой строки должны предшествовать символы обратной косой черты. Содержимое области шаблона удаляется, и последующие команды редактирования не могут быть применены К нему (или тексту). Пример # Заменить в файле первые 100 строк: 1,100с\ ...первая строка замещения\ ...вторая строка замещениях ...последняя строка замещения d [адрес!] [,адрес2] d Удалить из области шаблона строку (или строки), определяемую адресами. Такая строка не передается на стандартный вывод. Читается новая строка входных данных, а редактирование возобновляется с первой команды сценария (см. параграфы 34.04 и 34.18). Пример # Удалить все пустые строки: /~$/d D [адрес!] [,адрес2] D Удалить первую строку многострочной области шаблона, получившейся в результате выполнения команды N, вместе с символом новой строки и возобновить редактирование с первой команды сценария. Если эта команда удаляет последнюю строку в области шаблона, то читается новая строка входных данных, как это происходит в случае выполнения команды d (см. параграф 34.18). Пример # Удалить следующие друг за другом пустые строки, оставив только одну: N /A\n$/D } g [адрес!][,адрес2] g Скопировать в область шаблона содержимое области хранения (см. команды h и Н), удалив прежнее содержимое области шаблона (см. параграфы 34.13 и 34.16). Приведенный ниже пример демонстрирует простой способ копирования строк. Потоковый редактор sed 551
34.24 Пример Данный сценарий "накапливает" все строки, содержащие выражение Item:, и копирует их в обозначенное маркером место. Маркер при этом удаляется. /Item:/H /<Replace this line with the item list>/g G [адрес1][,адрес2] G Выполняет действия, аналогичные производимым командой д. Различие состоит в том, что область хранения вставляется после адресуемой строки, а сама строка не удаляется. Пример демонстрирует простой способ "вырезания и вставки" строк (см. параграфы 34.13 и 34.16). Пример Этот сценарий накапливает все строки, содержащие выражение Item:, и помещает их в обозначенное маркером место в файле. Первоначальные строки, содержащие выражение Item:, удаляются. /Item:/( Н d } /Summary of items:/G h [адрес!] [,адрес2] h Копирует область шаблона в область хранения — временный специальный буфер. Прежнее содержимое области хранения уничтожается. Прежде чем редактировать строку, ее рекомендуется сохранить, воспользовавшись командой h (см. параграфы 34.13 и 34.16). Пример # Отредактировать строку/ вывести изменения и опять вернуться # к исходной строке: /UNIX/! h s/.* UNIX \(.*\) .*/\l:/ P x 1 Образец ввода: This describes the DNIX Is command. This describes the UNIX cp command. Образец вывода: Is: This describes the UNIX Is command. cp: This describes the UNIX cp command. H [адрес!] [,адрес2] Н Добавляет содержимое области шаблона (которому предшествует символ новой строки) к содержимому области хранения. Даже если в области хранения ничего нет, команда н все равно добавляет символ новой строки (см. примеры для команд g и G, а также параграфы 34.13 и 34.16). 552 Часть пятая. Редактирование текста
34.24 i [адрес] i\ текст Перед каждой строкой, соответствующей адресу, вставляет текст. (Относительно использования параметра текст см. описание команды а.) В параграфе 43.20 показано, как команда i используется в сценарии. Пример /Item l/i\ The five items are listed below: 1 [адрес!] [,адрес2] 1 Выводит содержимое области шаблона, отображая непечатаемые символы в виде ASCII-кодов (si.03,25.07). Длинные строки переносятся. п [адрес 1] [,адрес2] п Читает в область шаблона следующую строку входных данных. Текущая строка передается на стандартный вывод, а следующая строка становится текущей. Управление передается команде, идущей за командой п, без возобновления выполнения сценария с самого начала. Пример Отформатированный с помощью макросов пакета ms (43.U) заголовок раздела располагается в строке, которая находится под строкой, содержащей макрос . ш. При необходимости вывести все строки текста заголовка вызовите команду sed -я, задав в ней следующий сценарий: /Л\.ш/{ п р } N [адрес!][,адрес2] N Добавляет к содержимому области шаблона очередную строку входных данных; две строки разделяются символом новой строки. (Данная команда предназначена для сравнения с шаблоном выражений, которые размещаются в двух строках.) Используя сочетание \п для сравнения со вставленным символом новой строки, можно сравнивать шаблоны с выражениями, содержащимися в нескольких строках (см. пример для команды D, а также параграф 34.15). Примеры Похож на предыдущий пример. Отличие состоит в том, что вместе с названием заголовка выводится и строка, содержащая макрос .NH: /A\.NH/{ N Р } Объединить две строки (символ новой строки заменяется пробелом): /Л\.ЫН/{ N s/\n/ / Р • } р [адрес!][гадрес2] р Потоковый редактор sed 553
34.24 Выводит строки с указанными адресами (или одну строку). Если опция командной строки -я не задана, то по этой команде выводимые строки будут дублироваться. Команда р используется также перед командами, которые изменяют ход выполнения сценария (d, N, b) и могут воспрепятствовать выводу текущей строки (см. примеры для команд h, n и N). Р [адрес1][,адрес2] Р Выводит первую часть многострочной области шаблона, созданной в результате выполнения команды N, вместе со вставленным символом новой строки. Действует аналогично команде р, если к строке не применяется команда N. Пример Следующий сценарий выводит каждую строку, содержащую слово word, а также одну предшествующую ей строку: N /word/P D q [адрес] q Выход из программы при достижении адреса. Строка с указанным адресом и текстом, добавленным к ней предшествующими командами а и г, выводится при условии, что не отменен вывод по умолчанию (см. параграфы 34.21 и 34.22). Примеры Удалить все после строк с указанным адресом: /Garbled text follows:/q Вывести только пятьдесят первых строк файла: 50q г [адрес] г файл Прочитать содержимое файла и расположить его после содержимого области шаблона. Между названием команды г и параметром файл должен быть вставлен ровно один пробел. Пример /The list of items follows:/r item_file s [адрес1] [,адрес2] s/шаблон/ Iстрока_замещения/[флаги] В каждой строке, соответствующей адресам, вместо шаблона подставляет строку_замещения. Если адреса задаются шаблонами, шаблон / / представляет последний указанный шаблонами адрес. Могут быть заданы следующие флаги. л В каждой строке с указанными адресами заменить n-е вхождение шаблона. Значение параметра л может находиться в диапазоне от I до 512; по умолчанию оно равно 1 (см. параграф 34.11). g В каждой строке с указанными адресами заменить все вхождения шаблона, а не только первое. Р Вывести строку, в которой замещение выполнилось успешно. Если в строке произведено несколько замещений, будет выведено соответствующее количество ее копий. Флаг часто используется в сценариях при условии, что была задана опция командной строки -п (34.02). w файл Записать строку в файл, если было выполнено замещение. В сценарии может быть открыто до десяти отдельных файлов. 554 Часть пятая. Редактирование текста
34.24 Примеры использования команды s представлены в параграфах 34.07—34.10. Примеры Приведем несколько коротких сценариев с комментариями. # Изменить третью и четвертую кавычки на ( и ): /function/( s/"/)/4 s/"/(/3 } # Удалить в заданной строке все кавычки: /Title/s/"//g # Удалить первое двоеточие и все кавычки; вывести строки с результатами: s/://p s/"//gp # Изменить первое "if", оставив только "ifdef": /ifdef/!s/if/ if/ t [адрес!] [гадрес2] t [метка] Проверяет, была ли сделана в строке с указанными адресами хотя бы одна замена. Если замена была произведена, выполняет переход на строку, обозначенную меткой -.метка (см. команды b и :). Если метка не указана, управление передается в конец сценария (см. параграф 34.20). Команда t может быть использована таким же образом, как и оператор case (44.05) в интерпретаторе Bourne shell. Обычно проверяется каждый оператор case. Если один из них принимает значение TRUE, осуществляется выход. Пример Предположим, что необходимо заполнить пустые поля базы данных. Имеются строки: ID: 1 Name: greg Rate: 4 5 ID: 2 Name: dale ID: 3 А вы хотите получить следующее: ID: 1 Name: greg Rate: 45 Phone: ?? ID: 2 Name: dale Rate: ?? Phone: ?? ID: 3 Name: ???? Rate: ?? Phone: ?? Кроме того, необходимо узнать количество уже заполненных полей. С этой задачей может справиться такой сценарий (поля разделены знаками табуляции): /ID/( s/ID:..* Name: .* Rate: .*/& Phone: ??/p t s/ID: .* Name: .*/& Rate: ?? Phone: ??/p t s/ID: .*/S Name: ?? Rate: ?? Phone: ??/p } w [адрес!] [гадрес2] w файл Добавляет в файл содержимое области шаблона. Название команды w должно отделяться от параметра файл ровно одним пробелом. В сценарии может быть открыто до десяти отдельных файлов. Если файл еще не существует, команда создаст его; если файл существует, его содержимое будет перезаписываться каждый раз при выполнении сценария. Несколько команд записи, которые направляют свой вывод в один и тот же файл, добавляют данные в конец файла. Потоковый редактор serf 555
Пример # Запомнить в файле блоки утилит tbl и eqn: /A\.TS/,/A\.TE/w troff_stuff /A\.EQ/,/A\.EN/w troff_stuff x [адрес!][,адрес2] x Заменить содержимое области шаблона содержимым области хранения (см. описание команды h, а также параграфы 34.13 и 34.16). у [адрес 1] [,адрес2] y/abc/xyz/ Преобразовать буквы. Заменить каждую букву а буквой х, Ь — буквой у, с — буквой z и т.д. (см. параграфы 34.12 и 34.14). Пример # Изменить item 1, item 2, item 3 на Item A, Item В, Item С... /item [1-9]/y/il23456789/IABCDEFGHI/ — DG, из книги UNIX in a Nutshell (SVR4/'Solaris) издательства O'Reilly & Associates
35 Альтернативные способы редактирования 35.01 А почему, собственно, альтернативные? Есть много альтернативных способов редактирования, распространенных настолько широко, что пользователь вполне может захотеть реализовать их в сценариях. Примерами программ, которые выполняют такое редактирование, являются: • утилита fint (зш) и ее аналоги (35.09, предназначенные для выравнивания строк и форматирования абзацев; • сценарий recomment (ззм), выполняющий форматирование блоков комментариев в программах и сценариях; • сценарий behead (35.05), выполняющий удаление заголовков из почтовых и других сообщений; • сценарий center (35.0s), предназначенный для центрирования строк текста в файле. Кроме того, имеется ряд программ, которые реализуют некоторые полезные методы модификации файлов, но обычно редакторами не считаются: • утилиты split (35.09) и csplit (35.Ю) предназначены для разделения большого файла на несколько маленьких; • утилита tr (35.П) выполняет замену одних символов другими, в том числе и непечатаемыми, которые воспроизводятся восьмеричными числами; • утилита dd (З5.м, 35.12,35.13) осуществляет различные преобразования данных в файле; • утилиты cut (3514) и colrm (35.15) вырезают из файла колонки текста или поля, а утилита paste (3518) вставляет их обратно в файл, возможно, в другом порядке. В настоящей главе рассказывается не только об этих, но и о многих других утилитах. - TOR 35:02 Обработка текста с помощью утилиты Ш Один из основных недостатков утилиты fold (43.0s) состоит в том, что она разрывает текст в произвольном месте строки, а таковым может быть даже середина слова. Это достаточно простая утилита, основная функция которой — не допустить печатания длинных строк за пределами страницы. Утилита fmt способна справляться с более сложными задачами, поскольку оперирует такими текстовыми конструкциями, как абзацы. Перенос она осуществляет по словам (а не просто разбивает концы длинных строк), пустую строку воспринимает как конец абзаца. Альтернативные способы редактирования 557
35.03 Утилиту fmt можно использовать, в частности, для форматирования строк почтовых сообщений или строк файлов, которые редактируются с помощью редактора у/ (зо.з7). (Редактор Emacs имеет свои встроенные средства выравнивания строк.) Она также незаменима при написании программ для интерпретатора shell и во многих других случаях, когда приходится иметь дело со слишком длинными строками, не помещающимися на экране, или, наоборот, со слишком короткими строками. В одной из версий UNIX команда fmt используется для форматирования диска (по крайней мере, мне известен один случай ее использования с этой целью). Не запустите случайно эту команду! Проверьте документацию на нее и ознакомьтесь с описанными ниже эквивалентами утилиты fmt. Есть несколько различных версий утилиты fmt, одна оригинальнее другой. В общем случае при работе с ней предполагается, что: • абзацы отделены друг от друга пустыми строками; • если строка имеет отступ, то этот отступ будет сохранен; • строки вывода будут содержать приблизительно по 70 символов. Некоторые версии утилиты fmt имеют опцию, позволяющую установить длину строки. Так, команда fmt -132 (в некоторых версиях — fmt -1 132) переформатирует файл так, что строки будут вмещать не более 132 символов; • утилита fmt читает данные из файлов или со стандартного ввода. Строки направляются на стандартный вывод. ^^^И На компакт-диске содержится GNU-версия утилиты fmt. Есть и несколько версий этой Г ® 1 программы, распространяемых бесплатно. Многие версии имеют опции, используемые при ^Ц-4 работе с структурированными данными других типов. Опция -р (з5.м) применяется при Ш форматировании исходных текстов программ. (Если в вашей версии опция -р отсутствует, ту же самую работу выполнит сценарий recomment (зз.м), использующий возможности утилиты fmt и редактора sed.) Опция -s позволяет разделять длинные строки по пробельным символам, но не объединять короткие строки в длинные. В качестве альтернативы утилиты fmt можно создать свою (зз.оз) простую (возможно, чуть более медленную) команду, использующую редактор sed и программу nroff. Проявив немного фантазии, можно (путем написания программ для утилит nroff и/или tbl) создать, скажем, команду автоматического форматирования текстовых таблиц или маркированных списков. - JP, TOR 35.03 Альтернативы утилите fmt Вы не сможете работать с утилитой fmt, если сначала не изучите ее хорошенько. И, к сожалению, fmt доступна не во всех версиях UNIX. Правда, вы можете получить GNU-версию этой утилиты с прилагаемого компакт-диска. Ее также относительно легко эмулировать с помощью редактора sed (34.24) и утилиты nroff (43.1з). Использование этих двух утилит позволяет получить все преимущества более сложного форматирования и большей гибкости, предоставляемых макросами (43.1S) утилит sed и nroff. (Если вы создаете что-то действительно оригинальное, например таблицы, то наряду с утилитой tbt (43.1s) вам могут понадобиться утилиты col и colcrt (43.1S), выполняющие форматирование выходных данных программы nroff.) * [Утилиты tbl, nroff и col позволяют создавать таблицы всего за несколько действий. Таблицы могут быть как простыми, так и достаточно сложными. Их можно отправлять по электронной почте или печатать. Никакие сложные средства для их просмотра не требуются. Можно не беспокоиться о выравнивании ширины столбцов о таблицах или о переносе текста. Если же вы хотите, чтобы результат работы выглядел еще лучше, то тот же файл для утилиты tbl можно обработать утилитой groff W.m. К сожалению, с утилитой tbl работают очень немногие пользователи, несмотря на то, что она является мощным средством описания таблиц. — JP] % 558 Часть пйтэй. Редактирование текста
35.04 В качестве примера приведем следующий сценарий: #!/bin/sh sed 'li\ .11 72\ .na\ • hy 0\ •pi 1' $* 1 nroff Причина такой его сложности заключается в том, что по умолчанию в программе nroff устанавливаются некоторые параметры, значения которых необходимо изменить. В частности, предполагается, что страница будет одиннадцатидюймовой (66 строк), поэтому программа дополнит файл пустыми строками. Это положение несложно обойти, если в начале подлежащего переформатированию текста задать программе nroff директиву .pi 1 (длина страницы равняется одной строке). Кроме того, в программе nroff задается выравнивание строк; этот режим при желании можно отменить с помощью директивы .па. Вы также можете отменить режим автоматического переноса (.hy 0) и установить длину строки равной 72 символам, а не 65 символам, как задано по умолчанию для совместимости с программой fmt. Все эти директивы вставляются по команде sed li перед первой строкой входных данных. В более сложном сценарии можно было бы использовать опцию задания длины строки -пп и преобразовать ее в директиву .11 программы nroff. — TOR 35.04 Сценарий recomment: форматирование блоков комментариев программы Строки блоков комментариев программы обычно начинаются- с одного или нескольких специальных символов, как показано в следующем примере: # 1-я строка комментария # 2-я строка комментария # 3-я строка комментария Иногда совсем не просто добавить текст в одну из строк блока комментариев, поскольку новая строка получается слишком длинной, из-за чего приходится переносить ее на следующую строку, а это, в свою очередь, требует вставки начальных символов комментария. Утилита fmt (35.02), как уже говорилось, форматирует строки текстового файла. Но стандартная ее версия не очень поможет в наведении порядка в блоках комментариев программы: она не отличает символов комментариев в начале строк от текста этих комментариев. (Если утилита fmt имеет опцию -р, то она справится с этой проблемой; пример использования такой программы приведен ниже.) Сценарий recomment — это команда fmt, выполняющая обработку блоков комментариев. Предназначен он для пользователей, пишущих программы для интерпретатора shell и утилиты awk, а также на С или любом другом языке, который допускает наличие блоков комментариев, занимающих по нескольку строк. ^Г*^Н Сценарий recomment читает строки, которые пользователь подает ему на стандартный ввод. Гц] В сценарии проверяется первая строка и определяется, какие символы используются для ее L ~~"А комментирования (см. переменную $cchars, задающую перечень возможных символов; recomment обычно это пробелы, знаки табуляции, # или *). Затем эти символы из каждой строки удаляются, оставшийся блок текста передается утилите fmt, после чего вызывается редактор sed (34.24), который снова добавляет в текст убранные символы комментария. Я чаще всего вызываю сценарий recomment из редактора vi с помощью команд фильтрации (30.22) наподобие следующих: !}recomment переформатировать до следующей пустой строки 5!!recomment переформатировать эту и следующие 4 строки Альтернативные способы редактирования 559
35.05 Обычно в сценарии recomment утилите fmt позволяется выбирать ширину блока комментариев (как правило, она равна 72 символам). Задать другую ширину можно двумя методами. • Во-первых, это можно сделать в командной строке: recomment -50 • Во-вторых, вы можете присвоить значение переменной среды CBLKWID, задав максимальную длину строки текста комментария в символах. Так, в интерпретаторе С shell можно воспользоваться командой % setenv CBLKWID 50 Сценарий recomment является далеко не совершенным, но за неимением лучшего вам придется им пользоваться. Ниже приводится часть сценария, которая выполняет рассматриваемые действия. Первые две команды ищут символы комментария и определяют длину строки комментариев. Следующие три команды удаляют символы комментария, форматируют оставшийся текст комментария и добавляют те же самые символы в начало всех переформатированных строк: # НАЙТИ СИМВОЛЫ КОММЕНТАРИЯ, КОТОРЫЕ ИСПОЛЬЗУЮТСЯ В ПЕРВОЙ # СТРОКЕ, И ЗАПОМНИТЬ ИХ В ПЕРЕМЕННОЙ $comment: comment="'sed -n \"ls/A\ ([$cchars]*\).*/\l/p\" $temp-" # ОПРЕДЕЛИТЬ ДЛИНУ СТРОКИ КОММЕНТАРИЯ: expr4S.29 cwidth='expr "$comment" : ' .*'" # ПЕРЕФОРМАТИРОВАТЬ БЛОК КОММЕНТАРИЕВ. ЕСЛИ УСТАНОВЛЕНА # ПЕРЕМЕННАЯ $widopt, ВОСПОЛЬЗОВАТЬСЯ ЕЮ: colrm 1 $cwidth < temp I # УДАЛИТЬ В СТРОКАХ СИМВОЛ КОММЕНТАРИЯ, fmt $widopt I # ПЕРЕФОРМАТИРОВАТЬ ТЕКСТ И sed ,•s/л/$comment/,, # ВЕРНУТЬ СИМВОЛЫ КОММЕНТАРИЯ. Если в вашей системе нет утилиты colrm (3s.i5), измените третью с конца строку, чтобы вместо нее использовать утилиту cut (з.ч.м): cut -c'expr $cwidth + Г- < temp I # УДАЛИТЬ В СТРОКАХ СИМВОЛ КОММЕНТАРИЯ В результате вместо, предположим, команды colrm 1 3 создается команда cut -c4-. Некоторые версии утилиты fmt (как та, которая записана на компакт-диске) имеют опцию -р, выполняющую те же самые действия, что и сценарий recomment. Но в отличие от последнего команде fmt -p следует указывать, какие символы используются в начале комментария. В качестве примера приведем начало программы на языке С. Первым символом в строке комментария будет звездочка (*): % cat prog.с /* * Эта программа, load.ее, читает входной * файл данных. * Каждая входная строка добавляется в массив Node. */ % fmt -p '*' prog.с /* * Эта программа, load.ee, читает входной файл данных. Каждая входная строка * добавляется в массив Node. */ - JP 35.05 Удаление заголовков в почтовых сообщениях с помощью сценария behead Иногда при сохранении или повторной отправке статьи в телеконференцию или в процессе работы с почтовым сообщением (из) бывает необходимым удалить строки заголовка 560 Часть пятая. Редактирование текста
35.07 (Subject:, Receive: и др.). Сценарий behead может читать данные со стандартного ввода и из файлов и направлять их на стандартный вывод. Приведем несколько примеров. • Обработка сохраненных сообщений в командной строке: % behead rosg* | mail -s "Did you see these?" fredf • Удаление заголовка из статьи, полученной по каналу от программы (в данном случае от программы readnews), которая сама не может удалять заголовки: What now? [ynq] s- | behead > имя_файла Ниже предлагается частично адаптированный сценарий Артура Дэвида Олсона (Arthur David Olson): #!/bin/sh case $# in 0) exec sed 'l,/A$/d';; *) for i do sed 'l,/A$/d' "$i" done esac Сценарий построен с учетом того, что в почтовых сообщениях для отделения заголовка от текста самого сообщения используется пустая строка. Поэтому в сценарии просто удаляется весь текст от начала до первой пустой строки. - JP 35.06 Низкоуровневая обработка файлов с помощью утилиты dd Вам необходимо отбросить произвольное число байтов в начале файла? С этой задачей команда dd справляется неожиданно быстро. Предположим, вы хотите удалить из файла сто первых символов. Приводим команду, которая легко справляется с подобными задачами (конечно, утилите dd необходимо задать имя файла с помощью опцией if= или указать, что она получает данные по каналу): % dd bs=100 sJcip=l Можно также попытаться выполнить команду % dd bs=l sJcip=l Утилита добычно читает или пишет данные блоками по 512 байтов. Размер входного блока данных можно изменить с помощью опции ibs=, а размер выходного блока — с помощью опции abs=. Чтобы задать размеры обоих блоков, воспользуйтесь опцией bs=. Опция skip= определяет число блоков, которое необходимо пропустить в начале файла. А зачем это нужно делать? В параграфе 22.17 приводится интересный пример, связанный с шифрованием файлов; в параграфе 20.06 объясняется, как применять команду dd в сети, где имеется накопитель на магнитной ленте. Узнать, как файлы из кодировки ASCII преобразуются в файлы кодировки EBCDIC, вы можете из параграфа 35.12. В параграфе 35.13 демонстрируются другие случаи применения утилиты dd. - TOR 35.07 Сценарий offset: отступы в тексте Приходилось ли вам иметь дело с принтером, который выводит печатаемый текст у самого левого края бумаги? У вас при этом наверняка возникало желание сделать отступ, чтобы текст выглядел на бумаге или на экране более эстетично. Предлагаемый ниже сценарий читает данные из файлов или со стандартного ввода, направляет их на стандартный вывод Альтернативные способы редактирования 561 behead
35.07 и делает отступ, по умолчанию равный 5 символам. Предположим, нам требуется распечатать копию файла с именем graph на принтер 1р, задав отступ величиной в 12 символов: % offset -12 graph | lp Существуют и более простые способы выполнения подобного задания (например, с помощью утилиты awk (S3.nj). В сценарии используется оператор case интерпретатора Bourne shell, причем используется он довольно необычным образом, что может подсказать вам другие интересные идеи. #!/bin/sh # ОПРЕДЕЛИТЬ ВЕЛИЧИНУ ОТСТУПА (ЕСЛИ ОН ЗАДАН) И ПРОВЕРИТЬ, 'offset ' * является ли это значение допустимым: case "$1" in -[0-9]|-[0-9][0-9]) indent="$l"; shift ;; -*) echo "'basename $0': '$1' isn't number or is > 99." 1>&2; exit 1 esac # УСТАНОВКА ПО УМОЛЧАНИЮ: case "$indent" in "") indent=-5 esac # ВСТАВИТЬ ПРОБЕЛЫ ДЛЯ РЕДАКТОРА sed. # ПЕРВЫЙ ОПЕРАТОР case ВСТАВЛЯЕТ 10 ПРОБЕЛОВ; ВТОРОЙ - 1: s=" " # 10 ПРОБЕЛОВ case "$indent" in -?) ;; # МЕНЬШЕ 10 ПРОБЕЛОВ; ПРОПУСТИТЬ ОБРАБОТКУ -1?) pad="$s" ;; -2?) pad="$s$s" ;; -3?) pad=-$s$s$s" ;; -4?) pad="$s$s$s$s" ;; -5?) pad="$s$s$s$s$s" ;; -6?) pad="$s$s$s$s$s$s" ;; -7?) pad="$s$s$s$s$s$s$s" ;; -8?) pad="$s$s$s$s$s$s$s$s" ;; -9?) pad="$s$s$s$s$s$s$s$s$s" ;; *) "'basename $0': Help! \$indent is '$indent'!?!" I>s2; exit 1 ;; esac case "$indent" in -0|-?0) ;; # ПРОПУСТИТЬ ОБРАБОТКУ; ЧИСЛО ПРОБЕЛОВ КРАТНО 10 -1|-?1) pad ="$pad ■■ ;; -2|-?2> pad ="$pad " ;; -3|-?3) pad ="$pad " ;; -4|-?4) pad ="$pad " ;; -5|-?5) pad ="$pad " ; ; -6|-?6) pad ="$pad " ;; -7|-?7) pad ="$pad " ;; -81-78) pad ="$pad " ;; -9|-?9) pad ="$pad " ; ; *) "'basename $0': Help! \$indent is '$indent'!?!" 1>&2; exit 1 ;; esac # СНАЧАЛА МОЖНО ДОБАВИТЬ КОМАНДУ expand, ЧТОБЫ ПРЕОБРАЗОВАТЬ СИМВОЛЫ ТАБУЛЯЦИИ: sed "s/"/$pad/" $* 562 Часть пятая. Редактирование текста
35.09 Сначала переменной indent присваивается значение, определяющее величину отступа, которое задается как -12 или -5. На следующем этапе в сценарии определяется переменная pad, предназначенная для запоминания величины отступа. Один из операторов case проверяет первую цифру в переменной $indent с целью определить, сколько десятипробелъных блоков необходимо поместить в переменную pad. Следующий оператор case проверяет вторую цифру и добавляет еще несколько пробелов. Команда sed (34.24) добавляет пробелы в начало каждой строки. Если в файле есть знаки табуляции, измените последнюю строку таким образом, чтобы использовать команду expand или pr -e -t (41.04), и передайте результаты по каналу в редактор sed. expand $* I sed "s/V$pad/" - JP 35.08 Сценарий center: центрирование строк файла center В этом параграфе рассматривается написанный Грегом Уббеном сценарий утилиты awk, который применяется для центрирования текста в 80-символьной строке. Если ваша система понимает сочетание символов #_!_ (44.04,4S.03), этот сценарий может быть непосредственно передан утилите awk, минуя интерпретатор shell. В противном случае поместите его в сценарий (44.14): #!/usr/bin/awk -f ( spaces = "" for (i = 1; i < (80 - lenth($0)) / 2; i++) spaces = spaces " " print spaces $0 } Для каждой входной строки в сценарии выполняется команда print, в которой задается столько пробелов, сколько необходимо для центрировния строки. В редакторе vi можно пользоваться командой фильтрации (зо.22), что позволит центрировать строки во время редактирования. Вы также можете просто воспользоваться сценарием center из командной строки. Например: % center afile > afile.centered % sort party_list | center | lp - JP 35.09 Разбиение файлов в фиксированных точках: утилита split Большинство версий UNIX поставляется с программой split, осуществляющей разбиение больших файлов на файлы меньшего размера. Такая потребность может возникнуть в случае использования редакторов, которые не могут работать с большими файлами, или когда необходимо пересылать файлы больших объемов по электронной почте (а они бывают настолько большими, что некоторые почтовые программы отказываются иметь с ними дело). Предположим, что вы хотите переслать кому-то по электронной почте большой текстовый файл % Is -1 bigfile -г—г—г— 1 jik 139070 Oct 15 21:02 bigfile Выполнив для этого файла команду split, мы разобьем его на несколько файлов меньших размеров — каждый из них будет состоять (по умолчанию для большинства версий команды split) не более чем из 1000 строк: % is -1 total 283 -г—г—г— 1 jik 139070 Oct 15 21:02 bigfile -rw-rw-r— 1 jik 46444 Oct 15 21:04 xaa -rw-rw-r- 1 jik 51619 Oct 15 21:04 xab -rw-rw-r— 1 jik 41007 Oct 15 21:04 xac wc 29.06 % wc -1 x* Апьтернативные способы редактирования 563
35.09 1000 xaa 1000 xab 932 xac 2932 total Обратите внимание на используемую по умолчанию схему именования, при которой к букве х добавляются пары букв аа, ab, ас и т.д. Действующие по умолчанию установки можно изменить. Например, вы можете заставить утилиту split создавать файлы, содержащие по 1500, а не по 1000 строк: % rm х?? % split -1500 bigfile % Is -1 total 288 -r—r—r— 1 jik 139070 Oct 15 21:02 bigfile -rw-rw-r— 1 jik 74016 Oct 15 21:06_xaa -rw-rw-r— 1 jik 65054 Oct 15 21:06 xab Можно также дать указание использовать другой (не х) префикс имени: % rm х?? % split -1500 bigfile bigfile.split. % Is -1 total 288 -r—r—r— 1 jik 139070 Oct 15 21:02 bigfile -rw-rw-r— 1 jik 74016 Oct 15 21:07 bigfile.split.aa -rw-rw-r— 1 jik 65054 Oct 15 21:07 bigfile. split .ab Хотя работать с утилитой split описанным выше образом достаточно просто, а ее синтаксис стал почти универсальным, тем не менее в выполнении команды split в разных версиях UNIX существуют различия. Есть четыре базовых варианта утилиты split, поставляемых с различными реализациями UNIX. 1. Утилита split, которая может разбивать только текстовые файлы на сегменты по п строк или меньшего размера. ^^в 2. Утилита split, обычно называемая bsplit, которая может разбивать на сегменты по п символов <Ш) 1 только нетекстовые файлы. (Бесплатная версия этой утилиты имеется на компакт-диске.) ■^^ 3. Утилита split, которая может разбивать текстовые файлы на сегменты по п строк или '"' нетекстовые файлы на сегменты по п символов и которая пытается при этом автоматически определять, с каким файлом — текстовым или нетекстовым — она работает. 4. Утилита split, способная выполнять действия как над текстовыми, так и над нетекстовыми файлами (если обрабатывается нетекстовый файл, ей об этом нужно явно сообщить). Определить, какая у вас версия утилиты split, можно только с помощью описания, которое есть в системе. Из него же можно узнать и точный синтаксис вызова программы. Проблема возникает с третьим вариантом утилиты: хотя она претендует на звание "интеллектуальной" и способной автоматически делать все, что требуется как для текстовых, так и для нетекстовых файлов, но иногда разбивает текстовый файл как нетекстовый или наоборот, выдавая совершенно неприемлемые результаты. Поэтому, имея в своей системе именно третий вариант утилиты split, вам, возможно, стоит заменить его каким-либо другим. Первый и второй варианты утилиты в общем-то справляются со своими "обязанностями", но они не заменяют друг друга, если в системе есть только один из них. Если требуется разбить нетекстовый файл, когда имеется только утилита split для текста, или разбить текстовый файл при наличии только утилиты bsplit, необходимо приобрести одну из упомянутых выше базовых версий, способных выполнять нужные функции. Из перечисленных вариантов утилиты четвертый является наиболее надежным и многофункциональным. Несколько версий этой утилиты имеется в различных архивах исходных программ, включая и бесплатно распространяемую версию для операционной системы BSD UNIX. И как альтернативный вариант: в случае инсталляции в системе программы perl (згоо 564' Часть пятая. Редактирование текста
35.10 нетрудно написать простой аналог утилиты split, пользуясь средствами языка Perl. Для того чтобы сделать это, не понадобится даже компилировать С-программу. Это преимущество особенно важно при выполнении утилиты split на различных платформах, где могут понадобиться разные исполняемые двоичные файлы. Если необходимо разбить нетекстовый файл, а у вас нет желания пускаться в поиски аналога утилиты split, который решал бы эту задачу, можно воспользоваться стандартной утилитой dd (35.06). Например, если бы файл bigfile, который использовался в предыдущих примерах, был нетекстовым и требовалось разбить его на сегменты по 20000 байтов, можно было бы задать приблизительно следующее: $ Is -1 bigfile Г* for 44.16 > 9.13 done < 45.23 $ for i in > do 1 jik 12 3 4 > dd of=x$i ba > done < bigfile $ Is -1 total 27 9 -rw-rw-r— -rw-rw-r— -rw-rw-r— -rw-rw-r— -rw-rw-r— -rw-rw-r— -rw-rw-r— 1 jik 1 jik 1 jik 1 jik 1 jik 1 jik 1 jik 1 jik 5 6 139070 Oct 7 :=20000 count=l 139070 Oct 20000 Oct 20000 Oct 20000 Oct 20000" Oct 20.000" Oct 20000 Oct 19070 Oct 23 08:58 bigfile 2>/dev/null 23 08:58 bigfile 23 09:0-0 xl 23 09:00 x2 23 09:00 x3 23 09:00 x4 23 09:00 x5 23 0'9:00 x6 23 09:00 x7 - JIK 35.10 Разбиение файлов с учетом контекста: утилита csplit Утилита csplit (контекстная утилита split), подобно утилите split (35.09), также позволяет разбить »"^и файл на несколько меньших по размеру файлов, но. она к тому же дает возможность получить Г (#1 1 корректные фрагменты различных размеров. При работе с утилитой csplit пользователь задает ^-._^ определенные места (номера строк или шаблоны поиска), по которым файл делится на cspA'f сегменты. Утилита csplit входит в System V, но есть также версии, распространяемые бесплатно. Рассмотрим для начала шаблоны поиска. Предположим, что у нас имеется документ, состоящий из трех основных разделов. Для каждого.из этих разделов можно создать отдельный файл с помощью команды % csplit outline /I./ /II./ /III./ 28 число символов в каждом файле 415 372 554 % Is outline хх00 заголовок документа и т.п. хх01 Секция I хх02 Секция II хх03 Секция III * Чтобы определить максимальное значение числа, разделите весь размер файла на размер блока, который хотите задать, и добавьте единицу, если есть остаток. В этом вам может помочь программа jot (4s.il). ** Размер выходного файла, который меня устраивает, задается в утилите dd с помощью параметра ba (block size — размер блока). Выражение 2>/dev/null (4S.ii) позволяет отказаться от диагностического вывода утилиты dd, который здесь совершенно не уместен и только занимает место. Альтернативные способы редактирования 565
35.10 По этой команде создаются четыре новых файла (файл outline остается неизменным). Команда csplit выводит количество символов в каждом из них. Обратите внимание, что первый файл (ххОО) содержит весь текст до первого шаблона, но без учета самого шаблона, а файл xxOl, как и ожидалось, содержит первую секцию. Именно поэтому числа в именах файлов начинаются с 00. (Даже если шаблон I. находится в самом начале файла outline, файл xxOl все равно будет содержать первую секцию, а файл ххОО в этом случае будет пустым.) Если не нужно, чтобы находящийся перед указанным шаблоном текст записывался в файл, наберите в качестве ограничителя шаблона % (знак процента): I csplit outline %I.% /II./ /III./ 415 372 554 % Is outline xx00 Секция I xxOl Секция II xx02 Секция III Текст, предшествующий первому шаблону, не выводится, а нумерация файлов изменяется. Внесем еще кое-какие изменения. Чтобы подавить вывод информации о количестве символов в файлах, воспользуемся опцией -s, а чтобы указать другой (не хх) префикс имен файлов — опцией -/ % csplit -s -f part, outline /I./ /II./ /III./ % Is outline part.00 part.01 part.02 part.03 Тем не менее одна небольшая проблема все еще остается. В шаблонах поиска . (точка) является метасимволом (см. параграф 26.10), соответствующим любому одиночному символу, так что шаблону /I. / могут соответствовать слова типа introduction. Специальное назначение точки следует отменить с помощью обратной косой черты. Однако обратная косая черта также имеет специальное назначение как в случае шаблона, так и в случае интерпретатора shell, поэтому необходимо либо применить два знака обратной косой черты, либо заключить шаблон в кавычки (».ы). Незначительная деталь? Да, но такая, что способна порядочно извести, если вы о ней забудете. Наша командная строка теперь будет выглядеть так: % csplit -s -f part, outline "AW" /II./ /III./ Файл можно разбить и по второму вхождению шаблона. Предположим, есть файл, в котором описано пятьдесят способов приготовления курицы, и необходимо каждый метод запомнить в отдельном файле. Каждая секция начинается заголовками WAY #i, WAY #2 и т.д. Чтобы разделить файл, воспользуйтесь аргументом "количество повторений" команды csplit. % csplit -s -f cook. fifty_ways /AWAY/ "{49)" Эта команда разбивает файл по первому вхождению слова WAY, а число в фигурных скобках "приказывает" утилите csplit повторить разбиение еще 49 раз. Обратите внимание, что символ А обеспечивает сравнение шаблона с началом строки и что в интерпретаторе С shell фигурные скобки (9.os) необходимо брать в кавычки. Приведенная ниже команда создаст пятьдесят новых файлов: % is cook.* cook.ОП cook.01 cook.48 cook.49 566 Часть пятая. Редактирование текста
35.11 Очень часто при необходимости разбить файл по повторяющемуся шаблону вы не знаете (и для вас это неважно), сколько именно файлов будет создано; вам достаточно иметь гарантию того, что нужное количество разбиений будет выполнено. В таком случае есть смысл указать счетчик повторений, который немного превышает задаваемое значение (максимальное значение — 90). К сожалению, если вы попросите утилиту csplit создать большее число файлов, чем это возможно, появится сообщение об ошибке Out of range (выход за пределы диапазона). Кроме того, если ошибка происходит при выполнении команды csplit, последняя завершается и удаляются все созданные ею файлы. ("Недоработка!" — воскликнете вы.) Выход из этого положения заключается в задании опции -к. Указанная опция поможет сохранить файлы, даже когда будет выдано сообщение Out of range. Утилита csplit позволяет разбить файл с отступом на некоторое число строк вперед или назад от строки, совпадающей с шаблоном. Например, чтобы разбить файл по строке, которая отстоит от строки со словом sincerely на пять строк, можно ввести команду % csplit -s -f letter. all_letters /Sincerely/+5 Такая потребность может возникнуть, в частности, при работе с несколькими деловыми письмами, объединенными в один файл. Каждое письмо всегда начинается через пять строк после строки со словом Sincerely в предыдущем письме. Приведем еще один пример, взятый из "Справочного руководства пользователя" для операционной системы UNIX компании AT&T: % csplit -s -k -f routine, prog.c '%main(%' '/")/+!' '{99}• Задача заключается в том, чтобы каждую из программ, содержащихся в файле prog.c, поместить в отдельный файл (routine.00, routine.01 и т.д.). В первом шаблоне используется символ %, так как мы хотим отбросить весь текст, идущий перед словом main. Второй аргумент можно интерпретировать так: "Найти в начале строки закрывающую фигурную скобку (обычно этот символ располагается в конце программы) и разделить файл в следующей строке (предполагаемое начало другой программы)". Повторите это разбиение до 99 раз,* используя для сохранения созданных файлов опцию -к. Кроме шаблонов в качестве аргументов можно задавать и номера строк. Так, команда % csplit stuff 50 373 955 "прикажет" создавать файлы, разбитые по заданным номерам строк. В этом примере новый файл ххОО будет содержать строки 1-49 (всего 49 строк), файл хх01 — строки 50-372 (всего 323 строки), файл хх02 — строки 373-954 (всего 582 строки); в файл ххОЗ будет помещено все остальное из файла stuff. Если указать количество повторений, утилита csplit будет выполняться подобно утилите split. Команда % csplit top_ten_list 10 "{18}" разобьет файл topjenjist на 19 блоков, каждый из которых будет состоять из десяти строк." - DG 35.11 Обработка символов с помощью утилиты tr Утилита tr — это фильтр для преобразования символов, который читает данные со стандартного ввода (13.01) и либо удаляет определенные символы, либо преобразует один символ в другой. * В данном случае разбиение в действительности может повториться только 98 раз, поскольку мы уже указали два аргумента, а максимальное их количество — 100. ** Не совсем так! Первый файл будет содержать только девять строк (1-9), остальные — по десять. В этом случае лучше задать split -10 top_ten_list. Альтернативные способы редактирования 567
35. J J Наиболее общим случаем применения утилиты tr является замена каждого символа одной строки соответствующим символом другой строки. (Строка последовательных символов в кодировке ASCII (51.03) может быть - представлена диапазоном символов, разделенным дефисом.) Например, команда < 13.01 $ tr 'A-Z' 'а-х' < файл Версия Berkeley преобразует все прописные буквы в файле в строчные. Результат будет направлен на стандартный вывод. В версии утилиты tr, которая входит в System V, любой диапазон символов должен быть заключен в квадратные скобки. Другими словами, необходимо указать [a-z], а не просто a-z. И поскольку квадратные скобки имеют для интерпретатора shell определенное значение, их следует заключить в кавычки. Если вы не знаете, с какой версией утилиты работаете, предлагаем провести такой тест. Версия для Berkeley преобразует скобки [ ], поскольку они не рассматриваются в качестве операторов диапазона, в символы А: % echo ' (1 * I tr ' [a-z] ' А АА Версия Berkeley % echo '[]* I tr '[a-z]' A [] Версия System V Лишь в одном случае нет необходимости беспокоиться о различии между двумя версиями, а именно когда один диапазон символов преобразуется в другой и оба они содержат одинаковое количество символов. Так, следующая команда работает в обеих версиях: $ tr '[A-Z]• '[a-z]' < файл Обе версии Утилита tr, которая входит в Berkeley UNIX, преобразует скобку [ из первой строки в аналогичный символ второй строки; то же самое происходит и с символом ]. В версии для System V скобки [ ] используются как операторы диапазона. В обеих версиях диапазон A-Z преобразуется в соответствующий диапазон a-z. Правда, это возможно только при условии, что в обоих диапазонах имеется одинаковое число символов. Версия для System V обладает одной прекрасной особенностью — синтаксической конструкцией [а*п] (где л — это некоторая цифра), означающей, что строка будет состоять из л повторений символа а. Если значение л не задано или равно 0, оно рассматривается как бесконечно большое число. И в этом есть смысл, особенно если вы не знаете, сколько символов может быть включено в исходную строку. Как было сказано в параграфе. 3.0.22, подобные преобразования могут быть полезными и при условии, что они выполняются из редактора v/ с целью преобразования строки. С их помощью определенные символы можно также удалять. При задании опции -d из входных данных удаляются один или несколько символов, указанных в строке (специальные символы следует заключать в кавычки, чтобы защитить их от интерпретации в shell). Например, следующая команда передает на стандартный вывод содержимое файла, из которого убраны все разделительные знаки (она также служит оеобым примером использования кавычек (8.ы)): $ tr -d ",.!?;:"""•• < файл При задании опции -s (squeeze — сжать) утилита tr удаляет встречающийся несколько раз подряд символ, который указан во втором аргументе. В частности, команда $ tr -s •• " " ■" < файл создаст копию файла, в котором повторяющиеся пробелы будут заменены одним пробелом. Утилита tr также полезна при преобразовании документов, созданных в других системах, но перенесенных для работы в UNIX. Например, в параграфе 1.05 было сказано, что утилита tr может использоваться для замены символов возврата каретки в конце строк текстовых файлов, созданных на компьютере Macintosh, символами новой строки, которые должны присутствовать 568 Часть пятая. Редактирование текста
35.12 в тексте в UNIX. Утилита tr позволяет определять символы как восьмеричные значения, указывая перед числом символ обратной косой черты. Вот пример такой команды: $ tr '\015' '\012' < file.mac > file.unix Команда $ tr -d '\015' < pc.file удалит символ возврата каретки из пары символов "возврат каретки/новая строка", которая используется для обозначения конца строки на персональных компьютерах. (Эту утилиту целесообразно применять и для удаления избыточных символов возврата каретки из файлов, созданных программой script (si.osj.) В параграфе 29.10 приводится пример использования утилиты tr, предназначенной для разбиения предложений на слова. - TOR, JP 35.12 Преобразование текста из кодировки EBCDIC в кодировку ASCII И Насколько удачна стандартная утилита UNIX dd, я узнал при работе с магнитной лентой, которая содержала информацию в кодировке EBCDIC. Она, dd, великолепна, особенно когда читаешь данные с магнитных лент, записанных, не в UNIX. (GNU-версия этой утилиты dd имеется на компакт-диске.) Но лишь разобравшись в том, что такое блок-фактор на магнитной ленте из другой системы (20.06), вы сможете без проблем читать почти все ленты. Например, чтобы прочитать магнитную ленту, записанную в кодировке EBCDIC и подключенную к устройству /dev/rmtO, а затем преобразовать данные в кодировку ASCII и записать их в файл wasjbm, необходимо выполнить такую команду: % dd if=/dev/rmtO of=was_ibm lbs=800 cbs=80 conv=ascii По умолчанию команда dd читает данные со стандартного ввода и направляет их на стандартный вывод. Если вы хотите использовать конкретный файл или устройство, укажите их с помощью опций //= и о/— % dd if=was_unix of=/dev/rmtO obs=800 cbs=80 conv=ebcdic Есть еще опция conv=ibm, которая задает использование другой таблицы преобразования из кодировки EBCDIC в кодировку ASCII. В описании утилиты dd сказано: "Таблицы преобразования ASCII—EBCDIC взяты из стандарта, опубликованного в САСМ (ноябрь 1968 г.) и определяющего преобразование 256 символов. Преобразование ibm, хотя оно реже признается как стандартное, в большей мере соответствует некоторым рекомендациям фирмы IBM. К сожалению, универсального решения на этот счет не существует". Теперь перечислим те особенности работы команды dd, о которых никогда не следует забывать. • Чтобы выполнить преобразование из одной кодировки в другую, необходимо иметь возможность читать неструктурированные данные с устройства оо.оз), поскольку вполне вероятно, что на магнитной ленте задаются нестандартные размеры блоков, отличающиеся от принятых в UNIX для магнитной ленты. • Всегда нужно иметь информацию о блок-факторе для магнитной ленты из другой системы, чтобы можно было указать его утилите dd. • Если на магнитной ленте из другой системы содержатся несколько файлов, необходимо указать такое имя лентопротяжного устройства, чтобы после считывания файла не выполнялась перемотка на начало ленты (режим "close on rewind") (20.03) и можно было читать файлы, следующие за первым. Альтернативные способы редактирования 569
35.13 Следует упомянуть еще одну деталь, касающуюся утилиты dd: все числовые параметры, которые определяют какие-либо размеры, указываются в байтах, если не оговорено обратное. Причем различные множители могут задаваться с помощью ключевых символов: символ к означает умножение на 1024, b — на 512 (блок), w — умножение на 4 (слово). Можно также задать умножение на произвольное число, набрав между двумя числами разделитель х. - TOR 35.13 Другие варианты преобразования с помощью утилиты dd Утилиту dd можно использовать не только для преобразования из кодировки EBCDIC в кодировку ASCII (3S.12), но и для некоторых других целей: • для преобразований записей фиксированной длины в записи переменной длины (conv= unblock) и наоборот (conv=block); • для изменения порядка байтов в каждой паре байтов {conv=swab)\ • для преобразований прописных символов в строчные {conv=lcase) и наоборот (conv=ucase). Чтобы указать размер буфера для преобразования, когда опция conv= равна block или unblock, необходимо задать опцию cbs=. (Это также справедливо при выполнении преобразования из кодировки EBCDIC в кодировку ASCII.) В буфер преобразования помещается указанное число символов. В случае преобразований вида ascii и unblock концевые пробелы удаляются и к каждому буферу перед выводом данных добавляется символ новой строки. Если выполняется преобразование вида ebcdic, ibm или block, входные данные для достижения указанного размера буфера дополняются пробелами. - TOR 35.14 Вырезание колонок текста или полей с помощью утилиты cut Ш^"^Л В System V входит еще одна замечательная утилита, которая называется cut. Она позволяет | (#) 1 выбирать определенные колонки или поля из одного или нескольких файлов. Те, у кого К-„-4 этой утилиты нет, могут инсталлировать с компакт-диска ее бесплатную версию. cut+рк g команде cut задается либо опция -с, позволяющая осуществлять вырезание по колонкам, либо опция -/ дающая возможность вырезать по полям. (Поля разделяются знаками табуляции, если с помощью опции -d не указан другой разделитель полей. Не забудьте применить кавычки (s.u), если захотите задать в качестве разделителя пробел или другой специальный символ.) Колонки или поля, которые будут вырезаться, необходимо указать сразу же после опции, не разделяя их пробелами. Отдельные значения разделяются запятыми; для указания диапазона можно воспользоваться дефисом (например, 1-10, 15, 20 или 50-). Утилита cut очень удобна в использовании, что иллюстрируется следующими примерами. • Определить, кто вошел в систему, но выводить только регистрационные имена: who5/.(W % who | cut -d" " -fl • Выделить системные и настоящие имена пользователей из файла /etc/passwd/ (м.озу. % cut -d: -£1,5 /etc/passwd/ • Вырезать символы из одной колонки файла и вставить их в другую колонку того же файла, например вырезать из четвертой колонки и вставить в первую: paste 35.1S Ч cut -c4 файл | paste - файл - TOR, DG 57U Часть пятая. Редактирование текста
35.16 35.15 Вырезание колонок с помощью утилиты colrm Утилита colrm для операционной системы BSD UNIX является альтернативой утилиты cut (.15.14), хотя и обладает несколько ограниченными по сравнению с последней возможностями. Эта утилита может вырезать данные только по колонкам, но не по полям. Задается ей, как правило, начальное положение колонки, а указывать конечное не обязательно. Текст читается со стандартного ввода. (Утилита colrm не может читать файлы непосредственно; переназначайте ввод с помощью операторов < или I (Ш1).) Если вы зададите номер только одной колонки, будут удалены все колонки строки, начиная с указанной. Но если в аргументах заданы номера двух колонок, будут удалены все символы, находящиеся между начальной и конечной колонками включительно. Приведенная далее команда из выходных данных команды Is -I (22.02) в системе BSD UNIX выведет только права доступа (колонки с 1-й по 10-ую) и имена файлов (колонки с 45-й до конца строки, включая пробел перед именами): % Is -1 | colrm 11 44 drwxr-xr-x manpages -rw-r—r— misc.Z -rwxr-xr-x myhead Следующая команда уберет имя удаленного компьютера, если оно указано. Имя начинается в колонке 33 вывода команды who (simy. % who I colrm 33 - JP, TOR 35.16 Автоматическое форматирование колонок с помощью утилиты cols ^^^| Если вывод какой-либо команды "пробегает" по экрану, не помещаясь на нем целиком, то | (S> 1 его можно направить по каналу программе постраничного вывода (25.оз, 25.04). Когда строки КИ-4 текста сравнительно короткие, вы можете видеть большую его часть, переформатировав текст cols в несколько колонок. Разместить текст в виде колонок (35.17) можно с помощью утилиты рг. Но использовать эту утилиту не так-то просто, если необходимо, чтобы колонки располагались одна под другой, а не рядом. Утилите рг не хватает гибкости и в том случае, если нужно создать столько колонок, сколько помещается на экране компьютера: чтобы определить ширину колонок, вам придется найти самую длинную строку. В некоторых UNIX-системах имеются специальные программы для организации данных в колонки. Но их немного. Указанную задачу решает сценарий cols, который читает входной текст, находит в нем самую длинную строку и определяет для команды рг опции, дающие возможность создать столько колонок, сколько позволяет длина строки экрана. Сценарий cols известен еще под семью другими именами-ссылками ов.оз), а именно: с2, сЗ, с4, c5J сб, с 7 и с8, организующими выходные результаты в 2, 3, 4, 5, 6, 7 и 8 колонок соответственно. Так, при необходимости вывести в колонках, предположим, перечень слов с орфографическими ошибками вы должны задать команду % spell somefile | cols wordl word2 word3 word4 word5 word6 word7 word8 word9 wordlO wordll wordl2 wordl3 wordl4 wordl5 wordl6 По умолчанию сценарий cols и другие его модификации располагают введенные слова по строкам экрана. Это самый быстрый способ. Если необходимо, чтобы выходные данные располагались по колонкам, задайте опцию -d: % spell somefile | cols -d wordl word3 word5 word7 word9 wordll wordl3 wordl5 word2 word4 word6 word8 wordlO wordl2 wordl4 ' wordl6 Альтернативные способы редактирования 571
35.17 Сценарий будет читать данные из файлов, которые вы укажете; в противном случае данные будут поступать со стандартного ввода. В сценарии ширина экрана пользователя определяется переменной среды COLUMNS, если она задана. В противном случае вызывается программа tcap, предназначенная для чтения базы данных termcap. (При использовании в системе базы данных terminfo вместо программы tcap вызывается программа tput (41.10).) Если вы работаете в системе X Window, ширину окон в которой можно настраивать, добавьте в сценарий команду проверки вывода stty size или stty -p (42.04). Несколько деталей, касающихся программирования. Количество колонок (ис) определяется по имени сценария (с2, сЗ и т.д.). В случае вызова сценария с именем cols используется утилита awk (jj.ii), позволяющая найти самую длинную строку и вычислить количество колонок. (Для этого оператору case (44.05) приходится проверять переменную $0 (44.22)) Команда ехрг (45.28) выполняет остальные вычисления. Без флага -d командная строка программы рг, задающая параметры колонок, выглядит достаточно просто: pr -$nc -t -w$width -11 $temp В переменной $temp записано имя файла, содержащего входной текст. При использовании опции -d командная строка усложняется. Дело в том, что теперь в ней применяется команда wc -I (29M), подсчитывающая количество входных строк, а затем команда ехрг, которая должна разделить количество входных строк на число колонок и добавить единицу: pr -$nc -t -w$width -l'expr \( Vwc -1 < $temp\" / $nc \) + 1" $temp Обратная кавычка, которой предшествует обратная косая черта (V) (45.31), означает, что сначала выполнится команда wc -1 < $temp. Количество строк, которое выдает команда wc, будет использоваться в качестве параметра в командной строке программы ехрг. Результат выполнения команды ехрг определяет размер страницы. Если вам такая насыщенная командная строка не нравится, команды wc и ехрг можно перенести в отдельную строку и передавать вычисленные значения посредством переменных интерпретатора shell. Данный сценарий можно инсталлировать с компакт-диска или из сетевых источников (52.07). Если вы получаете его по сети, дайте утилите tar указание инсталлировать сценарий cols и все его ссылки: % tar xvf архив.tar cols с2 сЗ с4 с5 сб с7 с8 х cols, 2160 bytes, 5 tape blocks c2 linked to cols c3 linked to cols - JP 35.17 Форматирование колонок текста с помощью утилиты рг Утилита рг (43.07) позволяет красиво разместить печатаемый файл на странице: с полями сверху и снизу, с указанием имени файла, даты и номеров страниц. Она также может печатать текст по колонкам — по одному файлу в колонке или по нескольку колонок в каждом файле. При указании опции -/ на каждой странице убираются заголовок и поля. Это полезно, когда необходимо "вставлять" данные в колонки без каких-либо разрывов. По одному файпу в колонке: опция -т При указании опции -т все файлы, заданные в командной строке, читаются одновременно и каждый выводится в отдельную колонку: % pr -m -t filel file2 file3 The lines The lines The lines of filel of file2 of file'3 are here are here are here 572 Часть пятая. Редактирование текста
35.17 В качестве разделителя колонок утилита рг использует символы табуляции. Если вас это не устраивает, попробуйте пропустить вывод команды рг через команду expand (4iM). Во многих версиях утилиты рг имеется опция -sx, при задании которой разделителем колонок может служить только символ X. Один файл, несколько колонок: опция -п При указании опции -л файл будет печататься в несколько колонок, количество которых определяется числом и. Например, при задании опции -3 файл будет напечатан в три колонки. Файл читается строка за строкой, пока не будет заполнена первая колонка (по умолчанию в колонке содержится 56 строк). Затем заполняется вторая колонка, потом третья. Если в файле еще остается текст, заполняется первая колонка страницы 2 и цикл, таким образом, повторяется: % рг -3 filei Nov l 19:44 1992 filel Pagel Line 1 here Line 57 here Line 115 here Line 2 here Line 58 here Line 116 here Line 3 here Line 59 here Line 117 here Текст в колонках может быть расположен очень неравномерно, в частности, он может полностью поместиться в одну колонку, тогда как другие колонки останутся пустыми. Устранить такого рода проблему можно путем подбора значения опции, задающей длину страницы -/ (см. следующий параграф). Горизонтальное размещение строк: опция -/ Вы хотите размещать данные горизонтально, скажем таким образом (см. пример), чтобы первые три строки выводились в начале каждой из трех колонок, следующие три строки были в колонках вторыми и т.д.? % рг -11 -t -3 filel Line 1 here Line 2 here Line 3 here Line 4 here Line 5 here Line 6 here Line 7 here Line 8 here Line 9 here Воспользуйтесь для этого опциями -// (длина страницы равна одной строке) и -/ (без заголовков). Каждая "страница" будет заполняться тремя строками (или другим количеством строк, определяемым количеством столбцов). Опцию -/ необходимо задать, иначе утилита рг просто будет игнорировать любую страницу, длина которой не рассчитана на наличие колонтитулов. Это именно то, что вам нужно, если данные должны располагаться в колонках, не имеющих заголовков. Если у колонок должны быть заголовки, пропустите выводные данные команды рг через другую команду рг. % рг -11 -t -3 filel j рг -Ь filel Nov 1 19:48 1992 filel Pagel Line 1 here Line 2 here Line 3 here Line 4 here Line 5 here Line 6 here Line 7 here Line 8 here Line 9 here При задании опции -h filel имя файла будет вставлено в заголовок. Разбить текст на колонки можно, конечно же, и с помощью таких средств, как утилита awk (33.il) и программа perl (згор. Просмотрите в этой связи также нашу информацию об утилите paste (35. is) и еще раз об утилите cob (35.16). - JP Альтернативные способы редактирования 573
35.18 35.18 Вывод данных в несколько колонок Возникало ли у вас когда-нибудь желание вывести два или три файла одновременно? Это (•) ] можно сделать с помощью программы paste, входящей в System V (или ее бесплатной версии, имеющейся на компакт-диске). cut+paste Так, чтобы создать файл, содержащий в трех колонках данные из файлов х, у и z, необходимо задать команду % paste х у z > файл Если вы хотите, чтобы программа paste читала стандартный ввод, воспользуйтесь опцией - (чтение стандартного ввода) и повторите ее столько раз, сколько колонок необходимо получить. Например, чтобы с помощью команды Is, входящей в System V, вывести файлы в четыре колонки (а не в одну, как это обычно делает команда Is), воспользуйтесь командой % Is | paste - - - - Опцией - удобно также пользоваться в утилите cut (35.Щ. Эта утилита позволяет вырезать данные из одной позиции в строке и вставлять их в другую позицию в этой же строке. Отдельные потоки данных, которые соединяются в колонках, по умолчанию разделяются символом табуляции, но посредством опции -d можно задать другой разделитель. Причем, в отличие от опции -d, используемой с утилитой cut, в качестве разделителя можно указывать не один символ, а целый ряд символов, которые будут использоваться по очереди. (Правда, я не знаю, в каком случае такую возможность следует применять; может быть, вы подскажете?) Символом в таком списке может служить любой обычный символ или один из следующих специальных символов: \п новая строка \ t табуляция \\ обратная косая черта \0 пустая строка Обратная косая черта (s.h) используется и при необходимости защитить символы от интерпретации в shell. В утилите paste также имеется опция -s, которая позволяет объединять последовательные строки из одного файла. Например, следующая команда позволяет объединить каждую пару строк в одну строку: % paste -s -d"\t\n" list - TOR, DG 35.19 Объединение строк с помощью утилиты join Если вы работали с базами данных, то наверняка знаете, в каких случаях используется утилита join. He исключено,, что вы также применяли ее для объединения двухколоночных файлов. По команде join в файле ищутся определенные колонки и "склеиваются" те их строки, значения которых совпадают. Проще всего сказанное продемонстрировать на примере. Предположим, что мне необходимо собрать сведения из нескольких тысяч сообщений электронной почты, полученных с помощью почтовой системы МН. Сделать это в системе МН нетрудно: там имеется команда scan, позволяющая получить в заданном формате почти всю необходимую информацию о каждом сообщении. Но чтобы подсчитать количество строк в сообщении, мне приходится пользоваться командой wc -I (29.06). Таким образом, я получаю два файла: один содержит вывод команды scan, а другой — вывод команды wc. В строках каждого из файлов имеется одно поле, содержащее номер сообщения. При необходимости отсортировать файлы по этому полю можно использовать утилиту sort. С помощью команды awk ' {print $1 ", " $2}' я разделяю запятой поля в строках вывода команды wc. Затем я задействую команду join, чтобы по полю "номер сообщения" соединить две строки вместе. 574 Часть пятая. Редактирование текста
35.20 (Используя этот файл на персональном компьютере, я обрабатывал его в СУБД dBASE. Но это уже другая история.) Ниже приведен фрагмент файла, полученного с помощью команды scan. Колонки, содержащие номер сообщения, адрес электронной почты, комментарий, имя и дату отсылки, разделяются запятыми: 0001, andrewe@isc.uci.edu,,Andy Ernbaum,19901219 0002,bc3170x@cornell.bitnet,,Zoe Doan,19910104 0003, zcode!postman@uunet.uu.net,,Head Honcho,19910105 А вот файл, полученный в результате выполнения команды wc и утилиты awt 0001,11 0002,5 0003,187 Посредством команды join мы можем объединить эти два файла по первой колонке (опция -t, указывает команде join, что поля должны быть разделены запятыми): % join -t, scanfile wcfile Результирующий файл выглядит следующим образом: 0001,andrewe@isc.uci.edu,,Andy Ernbaum,19901219,11 0002,bc3170x@cornell.bitnet,,Zoe Doan, 19910104, 5 0003, zcode!postman@uunet.uu.net,,Head Honcho,19910105,187 Конечно, утилита join может выполнить намного больший объем работы, чем показано в этом примере (см. документацию). - JP 35.20 Краткий справочник: утилита uniq Утилита uniq предназначена в первую очередь для удаления смежных строк-дубликатов в отсортированном файле. Причем копия строки-дубликата направляется на стандартный вывод или во второй файл, если таковой задан в командной строке. Будьте внимательны! По команде % uniq filel file2 строки, содержащиеся в файлах filel и file2 в единственном экземпляре, не будут направлены на стандартный вывод! По этой команде содержимое файла file2 будет заменено уникальными строками файла filel. Утилита uniq (ее опции перечислены ниже) часто используется как фильтр (ьзо). Просмотрите также информацию об утилитах comm (2s.i2), sort (Зб.оо и команде sort -и (36М). Опция Назначение -с Выводить каждую строку по одному разу, одновременно подсчитывая, сколько раз она повторяется. -d Выводить по одному разу строки-дубликаты, но не выводить строки, имеющиеся в единственном экземпляре. -и Выводить только строки, имеющиеся в единственном экземпляре (копирование строк-дубликатов не выполняется). -л Игнорировать первые п полей строки; поля разделяются пробелами или знаками табуляции- +п Игнорировать первые л символов поля. Альтернативные способы редактирований 57$
35.21 Одновременно можно указывать лишь одну из опций -с, -d или -и. Примеры Переслать в выходной файл list.new по одной копии каждой строки файла list. uniq list list.new Показать, какие имена появляются более одного раза: sort names I uniq -d Показать, какие строки появляются ровно три раза: grep 27.01 sort names I uniq -с I grep " 3 " — DG, из книги UNIX In a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates 35.21 Использование переменной IFS для разбиения строк Возможно, не всем понятно, для чего интерпретатору Bourne shell нужна переменная IFS (внутренний разделитель полей). Дело в том, что содержащиеся в ней по умолчанию три символа — пробел, знак табуляции и символ новой строки — используются интерпретатором shell для анализа командной строки. Если вы хотите разбить строку текста (скажем, из базы данных) на поля, воспользуйтесь переменной IFS. Временно поместите разделитель полей в эту переменную, с помощью команды set (44. щ задайте значения полей в качестве параметров командной строки, а затем восстановите старое значение переменной. В приведенном ниже фрагменте сценария пользователь по команде stty -g (42.04) получает текущие установки терминала, которые выглядят приблизительно так: 2506: 5 :bf: 8аЗЬ: 3:1с: 8:15:4 -.0:0: 0 :11:13:1а: 19:12 : f : 17:16:0:0 Интерпретатор shell анализирует строку, которую возвращает заключенная в обратные кавычки (9.16) команда stty, и запоминает значение переменной х в переменной $1. Благодаря этому выдача сообщений об ошибках, если выполнение команды stty по какой-либо причине закончится неудачно, будет прекращена. (Если вы не укажете значение переменной х, то в случае невыполнения командой stty стандартного вывода команда set интерпретатора выведет список всех переменных.) Далее в переменную $2 помещается подстрока 2506, в переменную $3 — подстрока 5 и т.д. Интерпретатор Bourne shell допускает использование не более девяти параметров (последним параметром является переменная $9); если входные строки состоят более чем из девяти полей, данный метод не годится. Но в приведенном сценарии используется интерпретатор Когп shell, в котором (как и в интерпретаторе bash) подобные ограничения не устанавливаются. #!/bin/ksh oldifs="$IFS" # В переменной IFS задать в качестве разделителя двоеточие(:) IFS=: # В $1 поместить х, а вывод команды stty -g поместить в переменные $2 - ${23} set х "stty -g' IFS="$oldifs" # Размер окна хранится в 16-м поле (не считая первого, содержащего переменную х): echo "Your window has ${17 ( rows". Поскольку для анализа вывода команды stty нет необходимости использовать подпроцесс, этот метод может оказаться более быстрым, чем применение внешних команд типа cut (35.14) или awlc (33.U). Однако в некоторых случаях использовать переменную IFS невозможно. Интерпретатор shell в качестве разделителя командной строки применяет пробелы, а затем символы, хранящиеся в переменной IFS. Но он не разделяет таким образом результаты подстановки значения 576 Часть пятая. Редактирование текста
35.22 переменной или подстановки результатов выполнения команды (9.щ. В качестве примера приведем три способа анализа строки, взятой из файла паролей /etc/passwd: % cat splitter #!/bin/sh IFS=: line='larry:Vk9skS323kd4q:985:100:Larry Smith:/u/larry:/bin/tcsh' set x $line echo "Case 1: \$6 is '$6'" set x 'grep larry /etc/passwd' echo " Case 2: \$6 is '$6'" set x larry:Vk9skS323kd4q:985:100:Larry Smith:/u/larry:/bin/tcsh echo " Case 3: \$6 is '$6'" % ./splitter Case 1: $6 is 'Larry Smith1 Case 2: $6 is 'Larry Smith' Case 3: $6 is 'Larry' В первом случае производится подстановка значения переменной, во втором — подстановка результатов выполнения команды, причем шестое поле включает и пробел. В третьем случае, хотя в командной строке применяется двоеточие, шестое поле разбивается: переменная $6 содержит слово Larry, а переменная $7 — слово Smith. Еще одна проблема возникает, когда какое-либо поле является пустым (как в подстроке larry: : 985 :100 :): интерпретатор shell "проглатывает" пустое поле, а переменная $6 содержит строку /u/larry. Последние две проблемы можно решить, воспользовавшись редактором sed и скобками (З4.ю). - JP 35.22 Выравнивание колонок Когда мы работали над данной книгой, я решил составить документ, содержащий названия всех параграфов, а также сведения о количестве строк и символов в каждом из них. Затем я объединил этот документ с информацией об авторе и кодом готовности параграфа. Задействовав команды wc -I -с (29.06), Ы_ (З5.н), sort рб.оо и join (35.19), я получил файл, который имел приблизительно такой вид: % cat messfile 2850 2095 51441 -ВВ.А sed tutorial 3120 868-21259 +ВВ mail - lots of basics 6480 732 31034 + How to find sources - JIK's periodic posting ...900 строк ... 5630 14 453 +JP Running Commands on Directory Stacks 1600 12 420 !JP With find, Don't forget -print 0495 9 399 + Make 'xargs -i' use more than one filename Как видите, прочитать это достаточно трудно. Поэтому предлагаю хотя бы выровнять колонки. Небольшой сценарий для утилиты awk (зз.п) обрабатывает беспорядочный набор символов: A sed tutorial mail - lots of basics How to find sources - JIK's periodic posting Running Commands on Directory Stacks With find, Don't forget -print Make 'xargs -i' use more than one filename Чтобы привести в порядок колонки, я использовал следующий простой сценарий и команду, вызывающую его: % cat neatcols { printf "%4s %4s %6s %-4s %s\n ", \ Альтернативные способы редактирования 577 19 9-171 % cat cleanfile 2850 3120 6480 5630 1600 0495 2095 51441 868 21259 732 31034 900 строк ... 14 453 12 420 9 399 -BB +BB + +JP ! JP +
35.23 rot $1, $2, $3, $4, substr($0, index($0, $5)) } % awk -£ neatcols messfile > cleanfile Данный сценарий несложно приспособить под любое количество колонок. Для тех, кто не знаком с утилитой awk, приведем о ней краткие сведения. • В первой строке оператора print/ между двойными кавычками (") указывается ширина полей и способ их выравнивания. Например, первая колонка выравнивается по правому краю и ее ширина равна четырем символам (%1s). Ширина четвертой колонки также составляет четыре символа, но выравнивается она по левому краю (%-4s). Пятая колонка настолько широка, что для нее трудно установить размер. Я использую формат для вывода строк (%s) вместо формата для вывода десятичных чисел (%d) — в этом случае утилита awk не станет удалять в колонках ведущие нули. • Во второй строке оператора print/ задается порядок вывода данных. В нашем случае выходные данные располагаются в той же последовательности, что и входные (но я мог бы расположить их и по-другому). В первых четырех колонках размешаются первые четыре поля — 51, $2, $3, 54. Пятая колонка — это место, куда я помещаю все остальное. Выражение substr($0, index ($0, $5)) означает "найти пятую колонку входных данных; напечатать ее и все, что находится после нее". - JP 35.23 "Вращение" текста Не исключено, что вы не раз, посетив барахолку или автомобильный рынок, говорили про себя: "Вот здорово! Это может когда-нибудь пригодиться; правда, я не представляю, для чего". Подобные мысли посещают нас и тогда, когда мы копаемся в каталогах с бесплатными программами. Аналогичным образом, т.е. совершенно случайно, мне попала в руки программа rot, выполняющая "вращение" текстовых колонок и строк. Приведем пример. В первой колонке представленного ниже листинга содержится входной файл. В трех других колонках показан тот же файл, пропущенный через программу rot один, два и три раза: $ cat file $ rot file abcde 54321 1 a 2 b 3 с 4 b 5 e $ rot file | rot 5 4 3 2 1 edcba дий команд rot и $ rot file I tail -r e d с b a 54321 $ rot file | rot | rot e d с b a 12345 tail -r (25.19): $ tail -r fi: rot 12345 a b с d e abcde 54321 1 a 2 b 3 с 4 b 5 e Программа rot "поворачивает" текст на 90 градусов, команда tail -r размещает его "вверх тормашками" — последняя строка становится первой, предпоследняя — второй и т.д. Программа rot также может "поворачивать" выходные данные команды banner (43.ii), в результате чего текст при печати располагается вдоль страницы, а не поперек). Теперь, надеемся, вы имеете представление о возможностях этой программы. - JP, LM 578 Часть пятая. Редактирование текста
36 Сортировка 36.01 0 чем пойдет речь в данной главе Выполнить сортировку файлов в UNIX достаточно просто, не так ли? Да, просто, если вы хотите отсортировать только список отдельных слов или строки, начиная с первого символа- Но если необходимо произвести более сложную операцию сортировки, более сложным становится и вид команды sort, и уже не достаточно задать лишь % sort имя_файла В настоящей главе рассказывается: • как выбрать в строке отдельные поля, подлежащие сортировке (параграф 36.02); • как заменить разделитель полей — пробельный символ — любым другим символом (параграф 36.03); • о проблемах, с которыми может столкнуться пользователь, если поля разделены пробельными символами (параграф 36.04); • о различии между алфавитной и числовой сортировкой (параграф 36.05); • о том, как целесообразнее использовать опции утилиты sort (параграф 36.06). Но обсуждая вопросы сортировки, мы не ограничимся лишь описанием работы утилиты sort. Как и многие другие средства, относящиеся к инструментарию UNIX, утилита sort оказывается более мошной, если используется совместно с другими утилитами. В частности, ее применение одновременно с другими программами даст вам возможность: • отсортировать абзацы или другие фрагменты текста, состоящие из нескольких строк (параграф 36.07); • отсортировать строки, какими бы длинными они ни были (параграф 36.08); • отсортировать список имен по фамилиям, независимо от того, есть ли второе имя (параграф 36.08). - TOR 36.02 Поля для сортировки: как работает утилита sort Если не указано обратное, утилита sort делит каждую строку на поля по пробельным символам (по пробелам или знакам табуляции) и сортирует строки по полям в направлении слева направо. Это означает, что при сортировке она берет за основу самое левое поле. Но если эти поля одинаковы, утилита sort сортирует, ориентируясь на следующее поле. Предположим, что в вашем учреждении руководитель службы материально-технического снабжения создал файл наподобие предложенного на следующей странице. Сортировка 19* 579
36.02 supplies furniture kitchen kitchen supplies furniture furniture supplies pencils chairs knives forks pens couches tables paper 148 40 22 20 236 10 7 29 Вы же решили все элементы отсортировать по категориям, расположив их в каждой категории по алфавиту: % sort supplies furniture furniture furniture kitchen kitchen supplies supplies supplies chairs couches tables forks knives paper pencils pens 40 10 7 20 22 29 148 236 Конечно, не всегда надо сортировать в направлении слева направо. Опция +п командной строки служит указанием команде sort сортировать, начиная с поля п. Опция -л говорит о том, что сортировку следует прекратить на поле п. Не забывайте, опять же, что и поля нумеруются в направлении слева направо, причем нумерация начинается с 0.* Приведем еще один пример. На этот раз мы попытаемся отсортировать список номеров телефонов известных писателей, президентов и певцов: Robert M Johnson 34 4-0909 Lyndon В Johnson 933-1423 Samuel H Johnson 754-2542 Michael K Loukides 112-2535 Jerry О Peek 267-2345 Timothy F O'Reilly 443-2434 Предположим, что данный список, в соответствии со стандартными "правилами" составления телефонных справочников, необходимо отсортировать по фамилиям (последнему имени), по первому имени и по инициалу второго имени. Номер телефона при этом учитываться не должен. Поэтому мы можем начать сортировку со второго поля, прекратить ее, достигнув третьего поля, продолжить сортировать по нулевому и первому полям и остановить операцию сортировки на втором поле (фамилии). Задав такой порядок действий, мы получим следующий результат: % sort +2 -3 +0 -2 phonelist Lyndon В Johnson 933-1423 Robert M Johnson 344-0909 Samuel H Johnson 754-2542 Michael К Loukides 112-2535 Timothy F O'Reilly 443-2434 Jerry О Peek 267-2345 Несколько замечаний относительно этой команды. • Опция -3 необходима для того, чтобы дать команде sort указание после выполнения сортировки по фамилиям не производить сортировку по телефонным номерам. Если опция -Зне будет задана, строка Robert M Johnson появится перед строкой Lyndon В Johnson, поскольку в последней номер телефона имеет меньшее значение. Я подчеркиваю это, поскольку сам часто опасался, как бы не задать аргументы неправильно, и вынужден был искать соответствующую информацию в документации. 580 Часть пятая. Редактирование текста
3€:04 • Нет необходимости явно задавать опцию +/. Если поле, на котором следует прекратить сортировку, явно не указывается, после опции +0 применяется опция +/. • Если два имени полностью идентичны, нас, очевидно, не интересует, в каком порядке будут отсортированы оставшиеся поля. Однако для большей уверенности в конце списка опций поставим -2 Это должно интерпретироваться следующим образом: "После сортировки по инициалам второго имени никакая другая сортировка не производится". Здесь есть несколько моментов, о которых стоит упомянуть. Данная информация может никогда и не понадобиться, но будет нелишним, если она все таки сохранится где-то у вас в памяти. Во-первых, можно добавить любую операцию преобразования (удаления пробелов, числовой сортировки и т.д.) в конец спецификатора поля, указав таким образом на необходимость его сортировки. Используя предыдущий пример, предположим, что при наличии двух одинаковых имен номера телефонов нужно отсортировать в порядке возрастания. Это задание выполняет команда % sort +2 -3 +0 -2 +3n phonelist Опция +3п служит указанием выполнить числовую сортировку по четвертому полю. Если вас беспокоит наличие пробелов в начале поля (возможно, некоторые телефонные номера содержат коды городов), воспользуйтесь опцией +ЗпЬ. Во-вторых, в середине любого поля можно указать отдельные колонки для сортировки, используя обозначение +п. с, где л — это номер поля, а с — позиция символа в поле. И наоборот, обозначение -л. с следует понимать как необходимость остановить сортировку на символе, предшествующем символу с. При подсчете количества символов обязательно убедитесь, что используется опция -Ь (игнорировать пробельные символы), иначе будет очень трудно определить, какой символ был учтен, а какой нет. -ML 36.03 Изменение разделителя полей В параграфе 36.02 объяснялось, как утилита sort делит входную строку на несколько полей, используя в качестве разделителей пробельные символы (пробелы или знаки табуляции). Посредством опции -I используемый разделитель полей можно заменить другим. При необходимости отсортировать, скажем, регистрационные имена по названию интерпретатора shell, который используется при входе в систему, можно задать команду /etc.. wd 1.09 % sort -t: +6 /etc/passwd root:SndEKOs9H7Ylm:0:1 .-Operator: / : /bin/csh sys:*:2:2::/:/bin/csh jim:LjKwcUt816kZK:2391:1004:Jim 0'Callahan:/u/jim:/bin/csh bart:2DPD8rCOKBbUu:2665:1004:Bart Buus:/u/bart:/bin/tcsh tap:xY7oeuJ8WxyGO:2943:1004:Tap Bronman:/u/tap:/bin/tcsh Опция -/: служит команде sort указанием использовать в качестве разделителя полей двоеточие. В данном примере поле 0 — это регистрационное имя, поле 1 — зашифрованный пароль, поле 2 — идентификационный номер и т.д. Согласно такой нумерации, имя интерпретатора shell, который используется во время входа пользователя в систему, будет указано в шестом поле. Не забывайте, что нумерация полей начинается с 0, так как подобная забывчивость является причиной очень многих проблем. Два последовательно набранных символа двоеточия указывают на пустое поле, которое также должно учитываться. -ML, TOR 36.04 Путаница из-за разделителей полей — пробельных символов Кто-то, возможно, надеялся, что такая простая операция, как сортировка, будет выполняться относительно однотипно. К сожалению, это не так. Поведение утилиты sort часто непредсказуемо. Сортировка 581
36.04 Я, конечно, буду пытаться внести ясность в создаваемую ею путаницу, но в то же время я готов выслушать критику со стороны настоящих знатоков утилиты sort. Поэтому, если у вас появится новая информация по данной теме, мы внесем ее в следующее издание книги. При использовании утилиты sort первое затруднение возникает при попытке определить, где заканчивается одно поле и начинается другое. Проще всего задать явный разделитель полей (Зб.оз). В этом случае будет легко сказать, где какое поле начинается, а где оно заканчивается. В утилите sort для разделения полей по умолчанию используются пробельные символы (знаки табуляции и пробелы), и правила интерпретации пробельных символов, к сожалению, здесь довольно сложные. Вот как я их понимаю. • Первый пробельный символ в строке является "разделителем полей"; он отмечает конец предыдущего поля и начало следующего. • Любой другой пробельный символ, который следует после разделителя полей, является частью нового поля. Значит, если в строке имеется два или более пробельных символов, первый из них служит разделителем полей и не учитывается при сортировке. Остальные же сортируются как часть следующего поля. • В каждом поле содержится, по крайней мере, один непробельный символ, если только не достигнут конец строки. (Другими словами, пустое поле может находиться только в конце строки.) • Пробельные символы не равнозначны между собой. Сортировка производится в соответствии с последовательностью сравнения, заданной стандартом ASCII (51.03). Следовательно, знаки табуляции сортируются раньше пробелов. Приведем простой, но достаточно наглядный пример, который иллюстрирует много трудных моментов, возникающих при сортировке. Мы будем сортировать файл sortme: apple Fruit shipment 20 beta beta test sites 5 Something or other Здесь не все так просто, как может показаться на первый взгляд. Команда cat -t -v (25.06,25.07) показывает, что в действительности файл выглядит следующим образом: ~Iapple~IFruit shipment 20"Ibeta/4Ibeta test sites S^I^ISomething or other л1 означает символ табуляции. Для начала разобьем наш файл на поля, очень внимательно применяя описанные выше правила. Использование в таблице кавычек позволит нам точно показать, где начинается и заканчивается каждое поле. Поле/Строка 0 1 1 "Чарр1е" "Fruit" 2 "20" "beta" 3 "5" "^Something" Теперь давайте попробуем отсортировать файл по разным критериям. Справа я буду делать примечания, указывая, на какой символ ориентируется утилита sort. Сначала мы будем сортировать по полю 0, т.е. по первому полю каждой строки. % sort oortma сортировка по полю О apple Fruit shipment лоле 0, первый символ: ТАБУЛЯЦИЯ 5 Something or other лоле 0, первый символ: ПРОБЕЛ 20 beta beta test sites лоле О, первый символ: 2 Мы уже знаем, что в схеме упорядочения знак табуляции предшествует пробелу. Все произошло так, как и ожидалось. Теперь давайте попробуем выполнить команду, в которой сортировка будет производиться по полю 1 (по второму полю): % sort +1 sortme сортировка ло полю 1 5 Something or other лоле 1, первый символ: ТАБУЛЯЦИЯ 2 "shipment" "beta" "or" 3 null (нет данных) "test" "other" 582 Часть пятая. Редактирование текста
36:05 apple Fruit shipment поле 1, первый символ: F 20 beta beta test sites поле 1, первый символ: Ь Опять же из-за символа табуляции в начале поля в отсортированном списке на первом месте появляется строка Something or other. Строка Fruit shipment предшествует строке beta test sites, так как в таблице ASCII прописные буквы предшествуют строчным. Теперь давайте отсортируем по следующему полю: % sort +2 sortme сортировка по полю 2 20 beta beta test sites поле 2, первый символ: Ь 5 Something or other поле 2, первый символ: о apple Fruit shipment поле 2, первый символ: s Здесь нет никаких неожиданностей. И наконец, отсортируем по полю 3 ("четвертому" полю): % sort +3 sortme сортировка по полю 3 apple Fruit shipment поле 3, NULL 5 Something or other поле З, первый символ: о 20 beta beta test sites поле З, первый символ: t Единственный "сюрприз" здесь состоит в том, что в отсортированном списке первым появляется поле NULL. Хотя, если разобраться, ничего удивительного в этом нет: NULL в таблице ASCII соответствует значению 0, поэтому и следует ожидать, что пустое поле будет на первом месте. Это был достаточно примитивный, но в то же время и трудный пример. Случайные догадки относительно того, что именно команда сортировки "должна делать", не в состоянии объяснить ни один из этих случаев. Если вы получаете задание отсортировать файл с очень запутанными данными, то рискуете поставить себя в безвыходное положение. Часто нужно не просто выполнить сортировку, но и спроектировать, кроме всего прочего, структуру файла данных. Если необходимо разработать формат входных данных, то, проявив небольшую предусмотрительность, вы избавите себя от большой головной боли. Имея возможность выбора, никогда не используйте в файле знаки табуляции. И будьте осторожны с пробелами в начале полей: слово, перед которым стоят пробелы, в отсортированном списке будет стоять перед другими словами. Поэтому предпочтение следует отдавать явным разделителям полей (наподобие двоеточия) или использовать опцию -Ь, с помощью которой команде sort дается указание игнорировать начальные пробельные символы. —ML 36.05 Алфавитный и числовой принцип сортировки Утилита sort выполняет две существенно различающиеся операции сортировки: алфавитную и числовую. Алфавитная сортировка выполняется в соответствии с традиционным "словарным порядком" с использованием последовательности сравнения, заданной таблицей ASCII (si.m). Прописные буквы предшествуют строчным (если только не указана опция -/ согласно которой прописные и строчные буквы не различаются) и могут перемежаться числами и разделительными знаками. Все это достаточно тривиально и вполне закономерно. Однако стоит поговорить о нестандартных ситуациях, поскольку именно они часто приводят к ошибкам в сценариях интерпретатора shell. Предположим, что выполняется сортировка чисел из диапазона от 1 до 12. Числовая сортировка приводит к упорядочению чисел по возрастанию их значений, как и следовало ожидать. А алфавитная сортировка дает следующее: 1 11 12 2 Сортировка 583
36.06 Да, именно так осуществляется сортировка чисел, если применяются правила, которыми пользуются при составлении словарей. Команды числовой сортировки могут оперировать десятичными числами (например, числами типа 123,44565778), но они не могут оперировать числами с плавающей запятой (типа 1,2344565778Е+02). А что произойдет, если мы включим буквы в данные, предназначенные для числовой сортировки? Результаты предсказуемы, но я бы все-таки сказал, что они "неопределенные". Числовой сортировки данных, содержащих буквы, следует избегать, ведь нет уверенности, что различные версии утилиты sort обработают эти данные одинаковым образом. Насколько мне известно, нет программ для сортировки шестнадцатеричных чисел. Напоследок еще одно замечание. В System V при числовой сортировке начальные пробелы обрабатываются как значащие символы. Поэтому числа с дополнительными пробелами перед цифрами в отсортированных данных будут находиться перед числами, у которых таких пробелов нет. Это ужасно глупое свойство программы. Способ обойти его существует: используйте опцию -Ь и всегда указывайте поле сортировки.* —ML 36.06 Различные советы относительно операции сортировки В данном параграфе рассказывается о наиболее интересных и полезных свойствах утилиты sort. Эта утилита действительно способна выполнить достаточно большой объем работ, если умело ее использовать. Работа с повторяющимися строками По команде sort -и можно отсортировать файл и исключить из него все повторяющиеся строки. Эта команда является более производительной по сравнению с командой uniq (З5.х), поскольку она, кроме всего прочего: • сортирует файл (в команде uniq предполагается, что файл уже отсортирован, а если это не так, ничего хорошего вы от нее не получите); • считается более гибкой. При выполнении команды sort -и строки рассматриваются как "уникальные", если совпадают значения полей, по которым производится сортиров- ка (36.02). Поэтому нет необходимости, чтобы строки были уникальными (в строгом понимании этого слова); различия, которые не связаны с полями сортировки, просто игнорируются. Есть, правда, кое-что, что может сделать команда uniq, но не способна выполнить команда sort. В частности, она может напечатать только те строки, которые не повторяются, или подсчитать количество вхождений каждой повторяющейся строки. Но в целом команда sort -и, на мой взгляд, более полезна. Могу дать совет относительно того, как целесообразнее использовать команду sort -и. При работе над справочным руководством мне часто приходилось создавать таблицы сообщений об ошибках. Наиболее просто это можно было сделать так: с помощью утилиты grep найти в исходном тексте программы операторы printf, написать несколько макросов для редактора Emacs (зг.оо, позволяющих исключить все лишнее; задействовать команду sort -и, чтобы получить список сообщений в алфавитном порядке и избавиться от повторяющихся сообщений, и, наконец, создать еще несколько макросов для редактора Emacs, формирующих собственно таблицу. После этого остается написать только пояснения. Игнорирование пробелов Опция -Ь (о которой я уже не раз упоминал) считается одной из самых важных. Посредством этой опции команде sort дается указание игнорировать лишние пробельные символы в начале каждого поля. На мой взгляд, опцию -Ь следовало бы .сделать опцией по умолчанию. * Еще одно глупое свойство: опция -Ь не работает, если с помощью опции +н не указано поле, по которому должна выполняться сортировка. 584 Часть пятая. Редактирование теша
36.07 И не следует забывать еще один момент: опция -Ь эффективна только в том случае, если явно указывается, по какому полю следует сортировать. Сама по себе команда sort -b является обычной командой sort, учитывающей наличие пробельных символов. Я считаю, что это ошибка разработчиков команды sort, разве не так? Сортировка, не чувствитепьная к регистру Если вы не делаете разницы между прописными и строчными буквами, вызовите команду sort с опцией ■/ (игнорировать регистр). При установке данной опции строчные буквы приравниваются к прописным. Другими словами, команда рассматривает все буквы как прописные. Сортировка, применяемая в споварях С помощью опции -d команде sort дается указание игнорировать все символы, за исключением букв, цифр и пробельных символов. В частности, команда sort -d игнорирует знаки препинания. Сортировка в порядке спедования месяцев При сортировке по месяцам команде sort посредством опции -М дается указание рассматривать три первых непробельных симно-?. поля как трехбуквенное название месяца. Таким образом, слово JAN (январь) находится перед FEB (февраль), а то, в свою очередь, находится перед словом MAR (март). Эта опция доступна не во всех версиях UNIX. Сортировка в обратном порядке Посредством опции -г команде sort дается указание сортировато в обратном порядке: буква Z будет предшествовать букве А, цифра 9 — цифре 1 и т.д. Как вы сможете убедиться, эта опция действительно полезна. Предположим, что у нас имеется выполняемая. в фоновом режиме программа, которая каждые сутки (в полночь) записывает информацию б количестве свободных блоков в файловой системе. Регистрационный файл для этой программы может выглядеть приблизительно так: Jan 1 1992: 108 free blocks Jan 2 1992: 308 free blocks Jan 3 1992: 1232 free blocks Jan 4 1992: 76 free blocks Приведенный ниже сценарий находит в регистрационном файле наименьшее и наибольшее количество свободных блоков: #!/bin/sh echo "Minimum free blocks" head25.20 sort -t: +lnb logfile | head -1 echo. "Maximum free blocks" sort -t: +lribr logfile I head -1 Этот сценарий нельзя отнести к разряду универсальных, но он достаточно ясно демонстрирует, что можно сделать с помощью команды сортировки. ч -ML 36.07 Сортировка элементов, состоящих из нескольких строк Утилита sort имеет существенное ограничение: она обрабатывает за операцию одну строку. При необходимости отсортировать файлы, элементы которых состоят из нескольких строк, пользователь оказывается в затруднительном положении. Допустим, мы должны обработать такой список адресов: Doe, John and Jane 30 Anywhere St. Сортировка 585
36.07 Anytown, New York 10023 Buck, Jane and John 40 Anywhere St. Nowheresville, Alaska 90023 р^^и Как же это можно сделать? Конечно, команда sort здесь не поможет. Что бы мы ни делали, I (•) 1 все будет заканчиваться появлением мешанины не соответствующих друг другу адресов, имен ^^^ и почтовых индексов. Прием, позволяющий справиться с этой задачей, описан в сценарии chunksort chunksort. Приведенный ниже фрагмент сценария делает всю работу, связанную с сортировкой адресов: # полностью пустые строки разделяют записи gawk '( gsub(/\n/,"\l"); print $0 "\1" ) ' RS= $files I sort $sortopts I tr '\1' '\12' Выполнение сценария начинается с обработки множества опций, которая здесь не отражается. Это в полном смысле слова всеохватывающий сценарий, позволяющий использовать любые опции команды sort, за исключением опции -о. В нем также содержится новая опция -а, которая дает возможность выполнять сортировку, ориентированную на различные строки многострочных элементов. Предположим, что выполняется сортировка файла адресов и название улицы содержится во второй строке каждого элемента. Команда chunksort -a +3 отсортирует файл, взяв за основу почтовый индекс. Я не считаю такой прием действительно нужным, но согласитесь, что это хороший пример использования дополнительных функциональных возможностей сценария. Тело сценария (следует после команд обработки опций) является достаточно простым. В нем используется утилита gawk (зз.п), способная образовать из многострочной записи единственную строку, содержащую символы CTRL-a в местах объединения прежних строк. После такого рода обработки те несколько адресов, которые мы взяли из типичного списка, могут выглядеть следующим образом: Doe, John and Jane"A30 Anywhere St."AAnytown, New УогкЛА10023ЛА Buck, Jane and John~A40 Anywhere St.^ANowheresville, Alaska^A90023~A Преобразовав исходный файл в список однострочных элементов, мы получили нечто, чем может оперировать команда sort. Поэтому теперь указанную команду можно использовать с теми опциями, которые были заданы в командной строке. Команда tr (35.1i) после сортировки "распакует" это однострочное представление записи и восстановит файл в его первоначальном виде, опять преобразуя каждый символ CTRL-a в символ новой строки. Обратите внимание, что сценарий утилиты gawk добавляет в конце каждой выходной строки по лишнему символу CTRL-a. Поэтому команда tr выведет дополнительный символ новой строки плюс еще один символ новой строки, который был добавлен оператором print утилиты gawk, отделяя пустыми строками каждый адресный элемент файла. (За эту идею следует поблагодарить Грега Уббена.) Существует много различных вариантов данного сценария. Вы можете, в частности, заменить команду sort командой grep, позволив последней вести поиск в многострочных элементах (например, искать адреса в адресном файле). Для этого потребуется немного другой порядок обработки опций, но сценарий, по сути, останется тем же. - JP, ML 586 Часть пятая. Редактирование текста
36.09 36.08 Сценарий lensort: сортировка строк по их длине Прекрасный сценарий, сортирующий строки в порядке возрастания их длины, используется в первую очередь при необходимости найти самое длинное слово: &юП 29.10 % deroff -w report | uniq -d I lensort uniq 35.20 an deoxyribonucleic Однажды я воспользовался этим сценарием, с тем чтобы отсортировать список путевых имен: find 17.01 % find adlr -type f -print | lensort adir/.x adir/.temp adir/subdir/partl/somefile adir/subdir/partl/a_test_case В сценарии используется утилита awk (зз.п), отвечающая за вывод информации о длине каждой строки, после которой указывается исходная строка. Для числовой сортировки (з«щ значений длины применяется команда sort. Редактор sed (34.24) удаляет данные о длине строк и пробелы, а затем печатает эти строки: • #! /bin/sh awk '{ print "%d\t%s\n", length($0), $0 }' | # Выполнить числовую сортировку строк, sort +0n -1 I # Удалить данные о размерах строк и пробелы; вывести эти строки, sed 's/A[0-9][0-9]* //' - JP 36.09 Сортировка списка людей по фамилиям Сортировать списки людей довольно трудно, так как в одних случаях и имена, и фамилии являются однословными (например, Joe Smith), а в других состоят из нескольких частей (например, Mary Jo Appleton). Предлагаемая ниже программа сортирует по последнему слову в каждом имени, т.е. по фамилии. Сценарий читает данные из файлов или со стандартного ввода и направляет их на стандартный вывод. #! /bin/sh # Вывести последнее поле (фамилию) , знак табуляции, а затем полное имя. awk '(print $NF "\t" $0}' $* | namesort # Отсортировать (по фамилиям: временно это поле является первым) . sort I # Удалить первое поле и напечатать имена, cut -f2- В параграфе 16.21 похожий прием используется для нахождения каталогов, имеющих одинаковые названия. - JP lensort и Сортировка 587
37 Язык Perl 37.01 Что мы расскажем и чего не станем рассказывать о языке Perl В отличие от других глав этой книги, в настоящей главе совсем немного говорится о том, что делает Perl или как его использовать. Программа perl вовсе не сложна в применении; язык Perl, интерпретатором которого она является, также легко изучить, особенно если вы знакомы с некоторыми из тех средств, для замены которых данный язык и предназначен. Но в то же самое время язык Perl настолько обширен и сложен, что даже составить краткий справочник наподобие того, который мы представили для редактора sed (34.24), нам было бы непросто. ■^^И Поэтому мы довольствуемся лишь двумя "аргументами" в пользу применения Perl (см. па- | <V 1 раграфы 37.02 и 37.04), оба из которых дадут вам понимание того, что ценного есть в этом fr-Ц-^ языке и почему многие пользователи стали такими пылкими его сторонниками. На рег15 компакт-диске имеется последняя версия языка Perl — Perl 5, о преимуществах которой мы обязательно поговорим. В настоящем издании содержится несколько сценариев на языке Perl. Если у вас есть желание изучить этот язык, обращайтесь к книгам Learning Perl, Programming Perl, а также к обширному интерактивному руководству, которое вместе с программой perl записано на компакт-диске. - TOR, JP 37.02 Зачем нужно изучать Perl (часть 1) Perl — это язык, предназначенный для манипулирования текстом, файлами и процессами. Он позволяет выразительно и наглядно выполнять многие задачи, которые раньше с трудом можно было реализовать на языке С или с помощью команд интерпретатора shell. Эта книга предназначена в первую очередь для пользователей UNIX, но язык Perl реализован в различных операционных системах и представляет пример обработки данных, обеспечивающей переносимость с одной платформы на другую. Первоначально Perl создавался как язык обработки данных, а именно: перемещения между файлами в произвольном порядке, эффективного просмотра текстов большого объема, запуска команд для получения доступа к динамическим данным и печати форматированных отчетов, использующих собранную из различных источников информацию. И справляется он с этими задачами вполне успешно. Благодаря своей способности осуществлять поиск по шаблону и обрабатывать текст Perl часто превосходит даже специальные программы, написанные на языке С. В процессе своего развития Perl стал языком, удобным для работы с файлами, т.е. таким, в котором можно выполнять операции с самими файлами, а не только с их содержимым: перемещать и переименовывать, изменять права доступа и т.д. Вместе с тем он стал языком, способным создавать и уничтожать процессы, управлять потоками данных, выполнять предварительную обработку входных данных и заключительную обработку выходных данных, а также "наводить порядок" после того, как процессы уничтожаются. 588 Часть пятая. Редактирование текста
37.02 И еще Perl стал языком работы в сетях, обладающим способностью устанавливать связь с другими процессами на других машинах посредством механизма гнезд (sockets). Все перечисленные операции можно выполнить и с помощью других языков, таких, например, как С, или команд интерпретатора shell. Но решение в таком случае часто бывает довольно сложным и недостаточно элегантным. Как известно, на С не так-то просто реализовать то многое, что позволяют делать команды интерпретатора shell, а при помощи последних не всегда можно справиться с задачами, выполнение которых не представляет труда для языка С. В какой-то мере программирование на Perl заполняет большую нишу между программированием в shell и программированием на С, позволяя пользователю без труда делать все, что доступно в обоих языках. С другой стороны, знание языка Perl может заметно облегчить изучение С, если именно такая цель стоит перед вами. А если вы, наоборот, уже знакомы с С, то освоение Perl не составит для вас труда, поскольку по своей структуре эти языки достаточно близки. Perl обладает многими свойствами, которые присущи утилитам UNIX, и это тоже может облегчить изучение как Perl, так и UNIX. Как говорится, не стоит изучать язык, если он не учит вас мыслить по-иному. Для тех, кто знает UNIX, Perl является исключением из этого правила, поскольку большинство его понятий по своей сущности являются развитием того, что уже есть в других компонентах UNIX. Для многих Perl просто служит практическим языком создания отчетов и выборок (Practical Extraction and Report Language). Для поклонников же Perl — это "патологически всеядная мусороуборочная машина" (Pathologically Eclectic Rubbish Lister). А для минималистов, которые считают, что должен быть только один метод выполнения какой-либо задачи, Perl выглядит безнадежно избыточным и вторичным. Но каким-то образом, вопреки минимали- стической философии инструментальных средств UNIX, язык Perl стал тем средством, которое способно решать многие задачи малой и средней степени сложности, и заслуженно занял подобающее место в ряду остальных инструментов. Perl можно назвать средством системного программиста, позволяющим создавать новые инструменты. Во многих отношениях Perl — это простой язык. Те типы данных и структуры, которые в нем используются, довольно легко применять и понимать, и часто бывает достаточно одного взгляда на хорошо написанный фрагмент программы, чтобы сказать, для чего она предназначена. Чтобы скомпилировать программу на языке Perl, вовсе не обязательно знать какие-либо особые магические формулы — достаточно выполнить ее подобно сценарию интерпретатора shell (45.оз). И нет также необходимости знать об этом языке все, чтобы начать писать на нем полезные программы. Несмотря на свою простоту, Perl является довольно богатым языком, в нем есть много такого, что следует изучить. На освоение всего этого, конечно же, придется затратить определенное время, но, получив в итоге доступ к столь обширным возможностям языка Perl, вы не станете об этом жалеть. Perl не только обладает многими способностями интерпретатора shell и языка С, но и включает в себя все возможности редактора sed и утилиты awk. Дело в том, что вместе с Perl поставляются специальные трансляторы, которые преобразуют старые сценарии редактора sed и утилиты awk (а также nawk и gawk) в сценарии на языке Perl. Благодаря этому можно наглядно увидеть, каким образом те свойства, с которыми вы уже познакомились в sed и awk, реализуются в Perl. Пользователям нравится Perl и по другим, чисто прагматическим причинам. Многие утилиты UNIX обладают недокументированными ограничениями: им не нравятся строки, в которых более п символов (и — это какая-то непостижимая степень числа 2), они "вылетают" при задании двоичных данных. Эти ограничения в какой-то мере обусловлены языком С, на котором написаны утилиты. Язык Perl, к счастью, не имеет подобных ограничений. Строки (и массивы) могут иметь сколь угодно большой размер. Какой угодно может быть глубина рекурсии вызовов подпрограмм. Не установлены ограничения на длину имен переменных. Не вызывает проблем и применение двоичных данных. Хэш-таблицы, которые используются ассоциативными массивами, расширяются по мере необходимости, что позволяет избежать потерь в производительности, и к тому же вы можете хранить их в файлах баз данных, называемых DBM-файлами. Язык Perl 589
37.03 Многие пишут на Perl и потому, что этот язык позволяет создавать более надежные программы. Применение механизма трассировки потока данных в языке Perl дает возможность определять, какие данные извлекаются из ненадежных источников, и не допускать опасных операций. Системным администраторам последнее свойство должно быть особенно по душе. Perl нравится многим еще и потому, что позволяет разрабатывать программы достаточно быстро. А если произойдет какой-либо сбой, вы немедленно получите соответствующее сообщение, поскольку это интерпретируемый язык сценариев. Вашу работу облегчит и встроенный текстовый отладчик, который понимает любое выражение на Perl, так как он сам написан на этом языке. Пользователи сначала называли Perl языком системного администрирования, поскольку он был "предметом обсуждения" в первую очередь системных администраторов. Но мы считаем, что Perl имеет более широкое назначение. [Язык Perl слишком сложный (или, как следовало бы сказать, очень полный), и описать его в этой главе практически невозможно. Поэтому мы рекомендуем воспользоваться пространным интерактивным руководством и программами, представленными на компакт-диске. Кроме того, вы можете приобрести книгу Programming Perl, написанную Ларри и Рендалом. В предисловии к этой книге (из нее взят и материал для данного параграфа) сказано: "Изучаете ли вы Perl в силу своей любознательности или потому, что получили такое указание от шефа, в любом случае это руководство проведет вас как через основы, так и через все сложности языка. И хотя мы не намерены учить вас профаммированию, вдумчивый читатель сможет почерпнуть для себя кое-что из техники и теории программирования. Вы много узнаете о системе UNIX, поймете, как установить баланс между созданием иитефированных пакетов и созданием наборов небольших независимых инструментальных средств. Мы будем поощрять вас к развитию в себе трех добродетелей профаммиста: лени, нетерпеливости и высокомерия. И приступая с этими намерениями к изложению материала, мы твердо уверены, что изучение языка Perl увеличит его значимость в ваших глазах." — TOR] — LW, RS, из книги Programming Perl издательства O'Reilly & Associates 37.03 Три добродетели программиста Лень Качество, побуждающее вас прилагать большие усилия, с тем чтобы уменьшить общие зафаты энергии. Именно лень заставляет писать профаммы, которые позволят в будущем сэкономить усилия и которые другие люди найдут полезными, а также заставляет документировать написанное таким образом, чтобы не появлялось слишком много вопросов. Следовательно, лень — это первая добродетель профаммиста. А также данной книги. Нетерпеливость Это гнев, который вы ощущаете, когда компьютер проявляет признаки лени. Из-за подобного гнева вы пишете профаммы, которые не просто реагируют на ваши потребности, но даже упреждают их. Или, по крайней мере, делают вид, что это так. Следовательно, нетерпеливость — вторая добродетель профаммиста. Высокомерие Чрезмерная гордыня, карающаяся отправлением в ад, является качеством, заставляющим писать (и сопровождать) профаммы, о которых другие не смогут сказать ничего плохого. Следовательно, высокомерие — третья добродетель профаммиста. — LW, RS, из книги Programming Perl издательства O'Reilly & Associates 37.04 Зачем нужно изучать Perl (часть 2) Будучи призванным выступить в роли защитника языка Perl, хочу для начала заметить, что, изучая новое, нельзя полностью забывать старое. UNIX — это плюралистическая среда, в которой одну и ту же проблему можно решить по-разному, хотя в некоторых случаях 590 Часть пятая. Редактирование текста
37.04 приходится идти окольными путями. Причем различные проблемы требуют разных подходов. И если вы примете для себя решение программировать только на Perl, не исключено, что написанные вами программы не всегда будут соответствовать поставленным задачам. А теперь позвольте открыться перед вами в своем истинном облике последователя языка Perl, а возможно, и его апологета. Вне всяких сомнений, Perl — это самая большая среди всех известных в сообществе UNIX программ, написанных на протяжении последних десяти лет. [Том выразил эту мысль приблизительно в 1992 году, но его мнение с тех пор, бьюсь об заклад, осталось неизменным. :-) — JP\ Благодаря языку Perl программирование снова стало приятным занятием. Язык настолько прост, что программирование можно начинать без особой подготовки, и в то же время он настолько богат, что позволяет решать самые разнообразные и сложные задачи. Я часто открываю в нем что-то новое, хотя и программирую на Perl почти ежедневно с тех пор, как Ларри Уолл опубликовал его для широкой публики приблизительно в 1991 году. Черт побери, иногда даже Ларри черпает из Perl что-нибудь новое для себя! Настоящий Мастер не всегда понимает всю глубину своего творения. Необходимо отметить и тот факт, что языки сценариев для редактора sed и утилиты awk являются подмножествами языка Perl; существуют даже трансляторы s2p и а2р для этих утилит. В Perl вы можете делать все, что позволяет делать интерпретатор shell, хотя Perl, строго говоря, не является командным интерпретатором. В большей степени это все-таки язык программирования. Большинство из нас писали или, по крайней мере, видели сценарии интерпретатора shell — настоящие исчадия ада. Часто рекламируемые в качестве образцов, демонстрирующих мощь системы UNIX, эти сценарии, будучи поначалу конгломератами маленьких одноцелевых инструментальных средств, быстро делались настолько сложными, что их становилось трудно понимать, модифицировать и сопровождать. После превышения определенного уровня сложности философия системы UNIX, состоящая в том, чтобы иметь много программ, каждая из которых хорошо делает что-либо одно, из ее достоинства превратилась в ее слабость. Самая большая проблема, связанная с использованием каналов для обмена данными между различными программами, заключается в том, что в конкретный момент времени может быть задействован только один такой канал. Это означает, что несколько различных потоков данных должны мультиплексироваться в единый поток, а затем демультиплексироваться на другом конце канала. При этом дополнительно тратятся процессорное время, а также человеческие усилия. Предположим, что вам необходимо переслать по каналу имена файлов, но вы хотите для одних файлов указать особые атрибуты (скажем, отметить, что им уже больше десяти дней), а для других не делать этого. Обычно при передаче такой информации после имени файла или перед ним помещается специальная маркерная строка. Значит, и программа, направляющая данные в канал, и программа, читающая из него, должны учитывать это. Не очень удачный подход. Поскольку perl — это программа, способная выполнить те же действия, которые выполняются дюжиной программ {sh, awk, sed, tr, wc, sort, grep и т.д.), обычно бывает проще и целесообразнее создать что-то целиком в perl, нежели, скажем, в sh или посредством определения псевдонимов команд. При этом, чтобы выполнить какое-либо задание, вам не нужно иметь множество каналов, временных файлов или отдельных процессов. Вам также нет необходимости "сбрасывать" данные команде tr, а затем принимать их обратно, передавать редактору sed и снова принимать, а потом ту же процедуру выполнять еще для утилиты awk, команды sort и опять для редактора sed. Как правило, такой порядок работы забирает много времени, очень неудобен, часто создает путаницу. Каждый, кто когда-либо пытался передавать аргументы командной строки в сценарий редактора sed хотя бы средней сложности, может подтвердить тот факт, что защита специальных символов с помощью кавычек и обратной косой черты — занятие не из приятных. Сказанное относится как к этапу составления сценариев, так и к этапу их чтения. В гетерогенной вычислительной среде доступные версии многих инструментальных средств при переходе от одной системы к другой различаются так сильно, что их нельзя считать полностью надежными. На всех ли ваших машинах интерпретатор sh работает с функциями Язык Perl 591
37.04 единообразно? А что вы скажете об утилите awk? Очень трудно разработать сложную программу, не разбив задачу на более простые подзадачи. Вы будете вынуждены для вызова сценариев других интерпретаторов shell обращаться к самим интерпретаторам и использовать порожденные процессы (Ж02) в качестве механизма вызова подпрограмм, а это, в лучшем случае, неэффективно. Для выполнения вашего сценария потребуется запустить несколько отдельных сценариев. И все это нужно инсталлировать, заставить работать, а затем поддерживать на всех различающихся между собой машинах, используемых в данной организации, — задача не из простых! В случае применения программы perl вам нужно лишь установить ее в своей системе (а это сделать достаточно просто благодаря наличию программы Configure, которую разработал Ларри) и идти домой отдыхать. Некоторые поставщики программного и аппаратного обеспечения начинают включать язык Perl даже в стандартные комплекты программных средств. И я предвижу, что такая тенденция в ближайшие несколько лет будет возрастать. В сравнении с sh, sed или, скажем, awk программа perl не только более быстрое, но и более мощное средство. Я знаю, что подобную мысль высказывают приверженцы многих программ, но это именно так и есть. Между возможностями программирования на языке С и программирования в интерпретаторе shell существует определенная ниша, которую очень удачно заполняют возможности perl. Требующие решения задачи с необычайной быстротой возникают в первую очередь в области системного администрирования. Поскольку системные администраторы постоянно имеют много работы и не могут тратить время на программирование каждой поставленной перед ними задачи на языке С, программа perl оказывается особенно полезной для них. Ларри Уолл, как известно, назвал свое детище "командным интерпретатором для программистов на С". Мне же больше нравится рассматривать его в качестве "языка BASIC для системы UNIX". Я понимаю, что под этим определением можно подразумевать как хорошее, так и плохое. Вам бы хотелось узнать, благодаря чему perl стала более мощной программой, чем отдельно взятые инструментальные средства? Причин тому имеется, достаточно много, и те из них, о которых дальше пойдет речь, не обязательно являются самыми важными. В первую очередь нужно отметить, что нам не приходится сталкиваться с досадными ограничениями на длину строковых данных, длину строки ввода или количество элементов в массиве. Все ограничения определяются только адресным пространством системы и размером виртуальной памяти. Возможности оперирования регулярными выражениями (26.04) в Perl не идут ни в какое сравнение со всем тем, что мне когда-либо приходилось видеть. Прежде всего, нет необходимости запоминать, какой особый тип регулярных выражений требуется для определенной программы, или жаловаться, что какая-то программа не допускает конструкции типа (. . | . .), опций +, \Ь или еще чего-то. Язык Perl обобщает в себе возможности языков сценариев всех остальных инструментальных средств, и это в полной мере относится к регулярным выражениям. В Perl имеется полнофункциональный текстовый отладчик (написанный, конечно же, на Perl), оказывающий неоценимую помощь при отладке сложных программ. Ни интерпретатор shell, ни все названное семейство программ (sed, awk, sort, tr и др.) не имеют ничего подобного. В языке Perl есть механизм управления циклами, еще более мощный, чем в С. В нем можно выполнить эквиваленты операторов break и continue (в Perl это last и next) для любого охватывающего цикла, а не только для текущего. Пользователь может даже применить что-то вроде оператора continue, который не должен в обязательном порядке выполнять повторную инициализацию цикла, но делает это по вашему требованию. Язык Perl оперирует и более богатыми в сравнении с командными интерпретаторами и утилитой awk наборами типов данных и операторами. В распоряжении пользователя имеются скалярные величины, массивы с числовыми индексами (списки) и массивы со строчными индексами (хэш-массивы). В них могут содержаться произвольные значения, включая числа с плавающей запятой, для обработки которых существуют встроенные математические подпрограммы и мощные операторы. Perl может оперировать двоичными данными произвольного размера. 592 Часть пятая. Редактирование текста
37.05 В отличие от языка LISP язык Perl позволяет генерировать строки, например с помощью функции sprintfQ, а затем применять к ним команду eval. Таким образом обеспечивается возможность генерировать код "на лету". Можно даже выполнять функции для создания других функций, которые будут вызваны позже. Переменные имеют динамическую область видимости, их можно передавать в подпрограммы; из подпрограмм можно возвращать данные любого типа. Поддерживаются полностью рекурсивные подпрограммы. Пользователям предоставляется встроенная программа автоматического генерирования форм с автоматической разбивкой на страницы, заголовками и центрированными текстовыми полями наподобие %(|fmt)s (вы, надеюсь, имеете представление об этой синтаксической конструкции). Существует механизм написания программ с установленным битом смены идентификатора пользователя (SUID) (1.23), которые благодаря усовершенствованному механизму распознавания "испорченных" данных, полученных из внешних источников, можно сделать безопаснее даже программ, написанных на С. Этот механизм не позволит вам поступить как-то действительно неразумно. В Perl у вас будет прямой доступ почти к любой системной функции или системному вызову, в том числе к ioctls, fcntl, select, pipe, fork, getc, socket, bind, connect, attach, косвенный доступ к вызову syscall, а также доступ к функциям getpwuid, gethostbyname и др. Здесь также есть возможность путем использования шаблонов преобразования структур данных считывать двоичные данные, создаваемые программами на С или системными вызовами. В то же время вы можете выполнять операции более высокого уровня, например операции проверки права записи и модификации файлов (44.20), которые осуществляются интерпретатором shell, а также использовать подстановку результатов выполнения команды (9.щ. Подстановочные знаки применяются в именах файлов посредством нотации <*. [ch] > (ls.oiy, функция низкого уровня readdirs выполняется по усмотрению пользователя. С DBM-файлами в Perl можно работать как с массивами. Это действительно превосходный способ обращения к системным базам данных (базам псевдонимов, новостей и т.д.), эффективный механизм доступа к большим наборам данных и поддержания их целостности. Не пугайтесь кажущейся сложности того, о чем я говорю. Язык Perl «а самом деле очень прост для изучения, поскольку в него включены многие из уже существующих программных средств. Он напоминает интерпретатор языка С, в который встроены программы sh, sed, awk и многие другие. Наконец, имеется ряд программ на Perl, написанных сторонними разработчиками. В частности, вы можете воспользоваться библиотеками работы с объектами. -ТС 37.05 А теперь поговорим о Perl 5 [Этот параграф содержит адаптированный материал, взятый из интерактивного руководства по языку Perl 5. — JPJ Другие параграфы этой главы на самом деле были написаны для предыдущей версии языка — Perl 4, но они применимы и к версии 5. Perl 5 — это почти полностью переписанная версия Perl 4. Большинство сценариев на языке Perl 4 будут работать и в Perl 5. А теперь поговорим о том, почему предпочтение следует отдать именно Perl 5. • Расширение функциональных возможностей. В Perl S появилась возможность писать более читабельные программы (более понятными стали и регулярные выражения). Сообщения об ошибках стали информативнее; существует режим выдачи дополнительных предупреждений, позволяющий определить, какие ошибки может допустить новичок. • Лексическая область видимости. Переменные Perl могут теперь объявляться в пределах лексической области видимости, как это делается в случае "автоматических" переменных языка С. • Произвольно вложенные структуры данных. Любое скалярное значение, в том числе и любой элемент массива, может теперь содержать ссылку на любую другую переменную или подпрограмму. Язык Perl 593
37.05 • Модульность и возможность многократного использования. Библиотека языка Perl теперь состоит из модулей, которые могут совместно использоваться различными пакетами. Пакет сам решает, импортировать ему весь объявленный интерфейс модуля или только его часть. Прагмы (т.е. директивы компилятора) определены и используются таким же образом. • Объектно-ориентированное программирование. Пакет в языке Perl может функционировать как класс. Механизм динамического множественного наследования свойств и виртуальные методы поддерживаются непосредственным образом, имеются только незначительные дополнения к синтаксису предыдущей версии языка. Дескрипторы файлов теперь могут трактоваться как объекты. • Встраиваемость и расширяемость. Язык Perl теперь может легко использоваться в прикладных программах, написанных на С или C++. Perl-подпрограммы либо сами вызывают подпрограммы на С, либо вызываются такими подпрограммами через документированный интерфейс. Существует препроцессор XS, который позволяет легко включать в Perl-программы подпрограммы на С и C++. Поддерживается динамическая загрузка модулей. • Совместимость с системой POSIX. Большая часть новых модулей поддерживает стандарт POSIX, что обеспечивает доступ ко всем подпрограммам и определениям POSIX через классы объектов, если это возможно. • Конструкторы и деструкторы пакетов. Новые блоки BEGIN и END предоставляют средства для захвата управления при компилировании пакета, а также после выхода из программы. Они работают точно так же, как и блоки BEGIN и END утилиты awk, когда используются ключи -р и -п. • Несколько одновременных DBM-реализаций. Программа на языке Perl теперь может обращаться к файлам баз данных систем DBM, NDBM, SDBM, GDBM и Berkeley DB из одного и того же сценария одновременно. • Дополнения к регулярным выражениям. Если вы считали, что регулярные выражения в предыдущих версиях Perl отличались богатством и полнотою возможностей, то вы будете приятно удивлены, познакомившись с Perl 5! - JP 594 Часть пятая. Редактирование текста
Часть шестая Управление процессами В одном из своих известных произведений поэт Уилльям Батлер Йейтс (William Butler Yeats) спрашивает: "В чем разница между танцором и танцем?" Если это не прозвучит слишком претенциозно, тот же вопрос можно задать относительно программ и процессов. Процесс — это образ программы, когда она выполняется, а не находится на диске. Во всей книге говорится именно о процессах. Мы ведем речь не о создании программ, а о нюансах их работы. Возможно, мы слишком усердствуем, выделяя отдельную часть для изложения данного материала. Параграфы этой части можно было рассредоточить по всей книге. Однако нам показалось целесообразным подчеркнуть не вполне очевидную связь между управлением процессами (глава 38), производительностью программ и системы (глава 39) и выполнением программ по расписанию (глава 40). - TOR
38 Запуск, останов и уничтожение процессов 38.01 0 чем рассказывается в этой главе Данная глава — это коллекция важных деталей, касающихся выполнения процессов. Процессами называются те программы, которые в данный момент выполняются, а не те, которые еще находятся на диске. Глава начинается с двух теоретических параграфов, в которых даются определения некоторым важным терминам. Далее речь пойдет о команде ps, позволяющей узнать, какие процессы выполняются в системе и сколько времени это занимает (параграфы 38.05, 38.06, 38.07). В следующих параграфах говорится о сигналах как способах взаимодействия процессов. Рассматриваются следующие вопросы: • Что такое сигналы (параграф 38.08)? • Как посылать сигналы с клавиатуры (параграфы 38.09, 38.10, а также 5.09)? • Как программы "обрабатывают" сигналы (параграфы 38.11 и 44.12)? Затем мы перейдем к способам уничтожения процессов: • Как уничтожить все процессы (параграф 38.12)? • Как уничтожить процессы, пользуясь их именами, а не идентификаторами (параграф 38.13)? • Как остановить вышедшие из-под контроля задания (параграф 38.14)? • Почему кажется, что некоторые процессы не завершаются, когда вы уничтожаете их (параграфы 38.15 и 38.16)? • Как убедиться, что все процессы завершились, когда вы выходите из системы (параграф 38.17)? • Как убедиться, что процессы не завершились, когда вы выходите из системы (параграф 38.18)? - TOR 38.02 Системные вызовы fork и exec В параграфе 1.11 уже рассматривались системные вызовы fork и exec, но они так часто упоминаются в данной главе, что мы сочли необходимым рассмотреть их детально и всесторонне. Запуск, останов и уничтожение процессов 597
38.03 Говоря кратко, fork и exec — это системные вызовы (запросы к сервисам ядра операционной системы), которые используются программами UNIX для создания новых процессов. При запуске операционная система UNIX начинает работу только с одним процессом — программой /л/7. Каким магическим образом процесс /л/7 превращается в сотни, а может быть, в тысячи процессов, которые составляют работающую ОС UNIX? Вот здесь и выступают на передний план системные вызовы fork и exec. Один процесс порождает другой (порождать — еще один термин, к которому вам следует привыкнуть), либо замещая самого себя (exec), либо, если он должен остаться в системе, создавая собственную копию (fork). В последнем случае копия, сформированная системным вызовом fork, благополучно самоуничтожается, запуская на выполнение (exec) требуемую программу. Хорошей иллюстрацией всей этой последовательности действий является процедура входа в систему UNIX (но не процедура сетевого (из) входа в систему). Процесс init порождает последовательность процессов getty, каждый из которых берет на себя управление последовательным портом (tty), следя за всем, что в нем происходит. Именно программа getty выводит приглашение login:. При вводе регистрационного имени программа getty теряет свои полномочия: она запускает команду login, используя системный вызов exec. Эта команда просит ввести пароль (если он установлен для пользователя с данным идентификатором) и, если пароль правильный, с помощью того же вызова exec запускает интерпретатор shell, применяемый во время регистрации в системе. Теперь каждый раз при запуске другой программы командный интерпретатор будет создавать свою копию с помощью системного вызова fork, а уже эта копия запустит вместо себя указанную программу посредством системного вызова exec. Этим объясняется, почему некоторые команды являются встроенными в интерпретатор shell (l.io). С запуском новых процессов связаны определенные издержки. Более того, поскольку порожденный процесс не может воздействовать на среду своего родителя (м.аз), некоторые команды не имеет смысла выполнять в отдельном интерпретаторе. Таковой, к примеру, является команда cd, которая не могла бы изменить рабочий каталог для текущего интерпретатора, если бы была отдельным процессом. Существует команда exec, которая вводится пользователем в командной строке (см. параграф 45.07). Однако будьте осторожны, поскольку эта команда заменит ваш интерпретатор shell любой другой командой, которую вы запустите с ее помощью, и не вернет интерпретатору функцию управления. Это полезно, если необходимо заменить один интерпретатор shell другим аналогичным интерпретатором (как описано в параграфе 22.22) или если пользователь готов выйти из системы, когда завершит работу команда, вызванная по запросу exec. - TOR 38.03 Управление процессами: общие сведения Как вы, очевидно, знаете, когда вы регистрируетесь в системе и начинаете вводить команды, вы ведете диалог с интерпретатором shell (s.oi). Используемый интерпретатор может быть вариантом интерпретатора Bourne shell (таким как стандартный sh, ksh или GNU-интерпре- татор bash) или интерпретатора С shell (таким как стандартный csh или tcsh, который позволяет редактировать текущую командную строку и вызывать предыдущие команды). Можно работать и с менее распространенным командным интерпретатором, например гс. Используемый вами интерпретатор shell — это процесс, одна из многих программ, одновременно выполняемых в системе. С каждым процессом связана определенная информация, а именно: • Идентификатор процесса (PID) — номер, присваиваемый процессу при запуске. Идентификаторы процессов являются уникальными. Они назначаются циклическим образом и, в конечном счете, могут повторяться, однако два одновременных процесса не могут иметь один и тот же идентификатор. 598 Часть шестая. Управление процессами
38.03 • Идентификатор пользователя (UID). Указывает, правами какого пользователя обладает данный процесс. С его помощью определяют, из каких файлов и каталогов процессу разрешено читать данные, а в какие — записывать (22.01). Кроме того, идентификатор пользователя позволяет указать, кто может уничтожить процесс (зя.ю) (т.е. приказать ему прекратить свое выполнение). • Идентификатор группы (GID). Напоминает идентификатор пользователя, но служит для указания, правами какой группы обладает процесс. В некоторых системах этот идентификатор определяет, какой группе будут принадлежать файлы, созданные данным процессом (см. параграфы 22.05, 22.13 и 22.02). • Среда. Среду образует набор переменных и их значений. Например, когда вы набираете команду echo $HOME по приглашению интерпретатора shell, она выводит имя начального каталога <i.20) и, таким образом, сообщает вам значение соответствующей переменной среды (6.01). • Текущий рабочий каталог (Н.оз) — это каталог, который в данный момент является каталогом по умолчанию. Когда вы указываете программе имя файла, но не задаете явно, где его искать [с помощью путевого имени (ы.02)— JP], программа просматривает текущий рабочий каталог, если только он значится в переменной PATH (см. параграф 6.04). • Дескрипторы файлов — это сведения о том, какие файлы процесс открыл для чтения и записи, а также о текущей позиции указателя чтения-записи в каждом файле. В параграфах 45.20—45.23 рассматривается использование дескрипторов файлов в интерпретаторе Bourne shell. • Группы процессов. Используются для распределения сигналов (звм, з«.09, зя.12) в версиях UNIX, поддерживающих управление заданиями (a.os). Группа процессов применяется также для контроля над тем, какой процесс может читать данные с терминала. Процесс, входящий в ту же группу, что и терминал, выполняется "в интерактивном режиме" и может читать данные с терминала. Другие процессы останавливаются при попытке произвести эту операцию. Когда пользователь вводит команды, интерпретатор shell является для вашего терминала управляющим процессом. Это означает, что интерпретатор представляет собой процесс, который принимает данные, вводимые пользователем (см. параграф 38.06). Как правило, когда по приглашению интерпретатора shell пользователь набирает команду, эта команда выполняется, и интерпретатор позволяет ей "захватить" терминал на время выполнения. Например, если вы введете команду more . login, чтобы просмотреть файл .login, интерпретатор shell запустит программу тоге <25.оз>, а сам войдет в режим ожидания до ее завершения. В процессе выполнения программы тоге можно вводить команды для постраничного просмотра файла. Программа тоге (но не интерпретатор shell) будет воспринимать их. Выполняемая команда называется порожденным процессом, или подпроцессом процесса shell, который является родителем порожденного процесса. Порожденный процесс наследует от своего родителя всю информацию о процессе (идентификатор пользователя, идентификатор группы и т.д.), исключая идентификатор процесса, поскольку порожденному процессу присваивается новый идентификатор. [Встроенные команды интерпретатора shell (l.io), такие как команда cd, не запускают порожденные процессы. — JP] Вполне естественной является ситуация, при которой интерпретатор shell находится в режиме ожидания, пока выполняется запущенная команда, и активизируется только по завершении процесса. Однако в некоторых случаях необходимо, чтобы командный интерпретатор не пребывал в состоянии ожидания. Приведем пример. Допустим, вы работаете с оконной системой, подобной X Window (Ui), и необходимо по команде xterm создать новое окно. В таком случае не следует просто вводить xterm, поскольку исходный интерпретатор shell будет ожидать до тех пор, пока не завершится команда xterm, и только после этого позволит вводить другие команды. Окажется, что можно работать только с одним интерпретатором shell, хотя на экране присутствуют два окна. Если вы не хотите, чтобы управление передавалось интерпретатору shell только по завершении процесса, запустите процесс в фоновом режиме (ив). Для этого в конце команды поставьте Запуск, останов и уничтожение процессов 599
38.04 символ &, например: xterms. Интерпретатор shell запустит порожденный процесс и после этого сразу же выдаст приглашение ввести другую команду. Обратите внимание на то, что в этой ситуации исходный интерпретатор сохраняет контроль над терминалом, и вновь созданный порожденный процесс не может читать входные данные. В некоторых интерпретаторах shell существует такая дополнительная возможность, как управление заданиями (п.т) (фоновыми заданиями, или просто заданиями, часто называют процессы, которые выполняются в фоновом режиме). Благодаря ей вы можете уничтожать задания или переводить задание из фонового режима в интерактивный, вследствие чего оно становится управляющим процессом терминала, данные для которого пользователь вводит с терминала. Важно помнить, что хотя информация о процессе наследуется порожденными процессами, когда они запускаются, с момента запуска родитель не может воздействовать на информацию порожденного процесса (и наоборот). Например, вы запустили редактор vi, а затем прервали его работу (П.М) и активизировали в интерпретаторе shell команду cd, чтобы изменить каталог. Редактор vi все равно будет работать с прежним рабочим каталогом, когда вы вернете его в интерактивный режим. Точно так же, если вы напишете сценарий, который изменит некоторые переменные среды, в интерпретаторе shell эти переменные сохранят свои старые значения, когда произойдет выход из сценария. Это иногда обескураживает пользователей MS-DOS, поскольку такая информация, как текущий каталог, запоминается в глобальной области, к которой имеют доступ все программы. Если порожденному процессу необходимо обмениваться информацией с родительским интерпретатором shell, потребуются другие методы (38.08, 44.23). [На мой взгляд, вам пригодится также следующая информация. Когда происходит выход из процесса, родительскому процессу возвращается числовой код завершения (44.07). В соответствии с соглашением нулевой код является признаком успешного завершения, а ненулевой — какой-либо ошибки. — JP\ Наряду со способами изменения среды и текущего каталога существуют способы манипулирования дескрипторами файлов (45.20,45.21, 45.22). - ЛК 38,04 Порожденные интерпретаторы shell Когда в UNIX одна программа запускает другую (точнее, когда один процесс запускает другой), новый процесс выполняется как подпроцесс (зв.оз), или порожденный процесс* Новый интерпретатор shell, запущенный другим интерпретатором shell, . называется порожденным.** Ну и что? Дело в том, что вы.должны знать некоторые важные детали, касающиеся порожденных процессов. Порожденный процесс получает копию переменных среды родителя. Какие бы изменения не производились в среде порожденного процесса, они не передаются его родителю. Тем не менее, я все равно слышу ваш вопрос: "Ну и что?" • Сценарии выполняются в порожденном интерпретаторе shell (если для запуска сценария не используются команды source и . (44.23)). Если сценарий производит изменения в среде порожденного интерпретатора shell, родительский интерпретатор не увидит эти изменения. Если в сценарии выполняется команда cd, она не изменит текущий каталог в родительском интерпретаторе shell. Изменение сценарием значения переменной среды TZ (или любой другой) (6.07) не вызывает изменения переменной 7Zb родительском интерпретаторе shell. Сценарий может устанавливать иное, чем у родительского интерпретатора, значение umask (22.04), и никаких проблем при этом не возникает. • Временами необходимо запускать порожденный интерпретатор shell из текущего интерпретатора. Возможно, у вас есть специальный проект, в соответствии с которым требуется работать в другом каталоге, переприсваивать значения переменным среды, устанавливать * Это не совсем верно, если подпроцесс запускается из родительского процесса с помощью вызова exec, а не Jiirk (см. параграф 38.02). '-■'■. ** Когда используется команда exec (45.от) интерпретатора shell, новый процесс ие создается. 600 Часть шестая. Управление процессами
38.05 новый начальный каталог, переопределять псевдонимы некоторых команд, устанавливать другое значение для переменной PATH (б.м) и т.д. Когда вы завершите работу в порожденном интерпретаторе shell, среда родительского интерпретатора останется такой же, как и прежде. . Если родительский интерпретатор поддерживает управление заданиями (12.0J), вы можете приостановить порожденный интерпретатор и вернуться в родительский, сохранив изменения в первом из них. Если порожденный интерпретатор также поддерживает управление заданиями, команда suspend (22.22) (или kill -STOP $£ (s.n)) приостановит его. В противном случае нажмите клавиши [CTRL-z], когда курсор находится в командной строке порожденного интерпретатора shell. Например: rhyprompt% csh prompt 7.02 myprompt% set prompt="project% " project% cd каталог project% satanv PRINTER plotter project% sat path=($path другяв_каталоги) project% satanv EXINIT "se ts=4 sw=4 aw мш=0" ...выполняется какие-то действия... project% suspend , Stopped myprompt% ...возврат в родительский интерпретатор shell... %csh 12.02 myprompt% fg %csh ...возврат в порожденный интерпретатор 3hell... % Я использую команду suspend так часто, что создал для нее псевдоним z, который напоминает команду [CTRL-z]. • При временном выходе в shell (30.26) запускается порожденный процесс. В среде этого процесса можно делать все, что угодно. Когда произойдет возврат из порожденного интерпретатора shell, все изменения исчезнут. • Команда su (22.22) запускает порожденный интерпретатор shell, в котором можно выполнять команду cd, изменять переменные среды и т.п. При активизации команды exit (выход) процесс, в котором выполняется интерпретатор shell, завершается. То же происходит, когда интерпретатор shell встречает в сценарии символ конца файла. В командной строке символ "конец ввода" (обычно — [CTRL-d]) вызывает аналогичное действие. В параграфе 44.11 объясняется, как команда exit устанавливает код завершения интерпретатора shell. - JP 38.05 Команда ps Команда ps создает отчет, содержащий итоговые статистические данные о выполнении текущих процессов. Команда ps без параметров выводит перечень идентификаторов процессов и имен терминалов, с которых были запущены команды, значение времени центрального процессора, использованного каждым процессом, а также названия самих команд. Вывод команды ps может выглядеть следующим образом (в разных системах он имеет свои особенности): PID TT STAT TIME COMMAND 1803 р5 IW 0:00 -csh (csh) 1883 p5 IW 0:04 vi outline 1811 p6 IW 0:01 -csh (csh) 5353 p6 TW 0:01 vi 4890 По умолчанию команда ps выводит перечень только тех процессов, владельцем которых вы являетесь. Часто необходим более полный перечень с достаточным объемом данных обо всех Запуск, останов и уничтожение процессов 601
38M процессах, выполняемых в системе в текущий момент. В системах BSD UNIX и System V опции, используемые в этой команде, различны. В BSD применяются опции -аих, с помощью которых создается таблица всех процессов, упорядоченных в порядке убывания значений времени использования центрального процессора. [Опция -а задает вывод списка процессов, принадлежащих всем пользователям, а опция -и — вывод более детальных данных. Опция -х служит указанием включать в перечень процессы, которые больше не имеют управляющего терминала (ЗйМ). — JP\ Часто полезно организовать конвейер между этой командой и командой head (25.20), отображающей на экране наиболее активные процессы: % ps -aux | head -5 USER martin chavez ng gull PID %CPU %MEM 12923 74.2 22.5 16725 10.9 50.8 17026 3.5 1.2 7997 0.2 0.3 SZ 223 1146 354 142 RSS 376 1826 240 46 TTY P5 P6 CO рз STAT R R N I S TIME 2:12 56:04 0:19 0:04 COMMAND f77 -o foo foo.F g94 Hg0.dat vl benzene.txt csh Значения полей этих выходных данных (а также других полей, выводимых при указании опции -/ команды ps) приведены в табл. 38.1. Первая строка демонстрирует, что пользователь martin выполняет компиляцию программы на языке FORTRAN (/77). PID (з&вз) этого процесса равен 12923, и в текущий момент процесс либо выполняется, либо готов к выполнению. Процесс пользователя с именем chavez (PID 16725) тоже либо выполняется, либо готов к выполнению, хотя имеет более низкий приоритет. В данном процессе выполняется программа g94. Вывод этой команды наглядно показывает, кто в настоящий момент использует большую часть системных ресурсов: в нашем примере таковыми являются пользователи martin и chavez, которые вдвоем используют приблизительно 85% времени центрального процессора и 73% памяти. С течением времени команда ps не усредняет значения %сри и %МЕМ. Таблица 38.1. Поля Попе' вывода команды ps Содержимое USER (BSD) и ID (System V) PID %CPU %MEM SZ RSS TT, TTY STAT (BSD), s (System V) Имя пользователя, которому принадлежит процесс Имя пользователя, которому принадлежит процесс Идентификатор процесса Доля времени центрального процессора (BSD), используемого данным процессом Доля используемой системной памяти (BSD) Используемая виртуальная память в килобайтах (BSD) или страницах (System V) Используемая реальная память (в тех же единицах, что и в случае sz) Порт терминала, связанный с процессом Состояние текущего процесса; может принимать одно (в системе BSD — несколько) из следующих значений: R — выполняемый или готовый к выполнению; S — ждущий; I — неактивный (BSD), промежуточное состояние (System V); Т — приостановленный (n.osy Z — процесс-зомби (заму, D (BSD) — ожидающий готовности диска; Р (BSD) — ожидающий подкачки страницы; X (System V) — расширяющийся, ожидающий дополнительной памяти; К (AIX) — доступный процесс ядра; W (BSD) — выгруженный на диск; N (BSD) — с повышенным значением параметра nice (39.09, 39.1 iy приоритет выполнения снижен; > (BSD) — приоритет выполнения искусственно повышен (39.ii) 602 Часть шестая. Управление процессами
38.06 Поле' TIME COMMAND stime (System V) с (System V), CP (BSD) F PPID PR I N1 WCHAN Содержимое Общее время использования центрального процессора Командная строка (возможно, усеченная), с которой началось выполнение процесса Время или дата начала процесса Текущий коэффициент загруженности центрального процессора; используется планировщиком для определения приоритета выполнения (значения PRI) Связанные с процессом флаги (см. интерактивное руководство по команде ps) PID родителя Фактический приоритет выполнения (вычисляется динамически) Значение параметра nice (зя.т процесса Событие, наступления которого ожидает процесс * Некоторые поставщики добавляют другие поля, например, для номера процессора при использовании мультипроцессорных систем и для дополнительных кодов состояния процесса (к примеру, поле К в системе AIX). У каждого поставщика могут быть свои коды. Например, код 0 в операционной системе Stardent UNIX служит для обозначения процесса, который в настоящий момент действительно выполняется (R означает '"готовый к выполнению"), в то время как 0 в системе А1Х является признаком несуществующего процесса. Приблизительно такой же список выдается командой ps -е/в System V: ps -ef UID PID PPID С STIME root 0 0 0 09:36:35 root 1 0 0 09:36:35 TTY TIME CMD ? 0:00 sched ? 0:02 /etc/init gull 7997 1 martin 12923 11324 chavez 16725 16652 ng 17026 17012 10 09:49:32 ttyp3 0:04 csh 9 10:19:49 ttyp5 56:12 f77 -o foo foo.F 15 17:02:43 ttyp6 10:04 g94 Hg0.dat 14 17:23:12 console 0:19 vi benzene.txt В колонках указаны имя пользователя, идентификатор текущего процесса, идентификатор родительского процесса (PID процесса, породившего данный процесс), текущее значение планировщика, время запуска процесса, связанный с процессом терминал, общее время использования центрального процессора и команда, которую процесс выполняет. Обратите внимание, что список упорядочен по значению PID, а не по проценту использования ресурсов. Версия команды ps для операционной системы AIX использует опции обеих систем — BSD и System V. Перед опциями системы BSD дефис не ставится (что допускается синтаксисом). Опциям System V всегда предшествует дефис. Таким образом, в системе AIX команда ps -au не является аналогом команды рз аи. Она идентична команде, применяемой в System V, хотя и выводит свои данные с заголовками колонок, как команда ps системы BSD. Поэтому вывод команды ps аих упорядочивается по возрастанию значений параметра PID, а не %сри. Команда ps полезна также при образовании конвейеров. Команда % ps -ашс | grep chavez позволяет определить, что в данный момент выполняет пользователь chavez- [В System V для этих целей предназначена команда ps -u chavez. — JP\ — AF, из книги Essential System Administration издательства O'Reilly & Associates 38.06 Управляющий терминал В параграфе 38.05 говорилось о том, что в команде ps необходимо задать специальные опции (-х в BSD и -е в System V), чтобы вывести список процессов, не и.меющих управляющего терминала. Запуск, останов и уничтожение процессов 603
38.07 Что же такое управляющий терминал? Это терминал, с которого запускается процесс. В выводе команды /ю управляющий терминал обычно указывается с помощью идентификатора fry. Этот выходной параметр команды ps, как правило, соответствует последовательному порту, или pt^ (4i.s). Устройство pry, или псевдотерминал, служит для обозначения структуры, посредством которой окно и процедура сетевого входа в систему о.зз) взаимодействуют с операционной системой, как если бы они являлись обычными терминалами. В выводе команды ps идентификатор терминала (tty) для /dev/ttyl может иметь вид tl, а для /dev/ttyp3 — рЗ. Применяются и другие обозначения, например со для /dev/console — полноэкранного устройства отображения рабочей станции, доступного до того, как на нем будет запущена какая-либо оконная система. В выводе команды ps для процессов без управляющего терминала в поле tty указываются вопросительные знаки. Каким образом процесс "теряет" свой управляющий терминал? Очень просто. Некоторые процессы, такие как системные демоны (i-М), вообще его не имеют: они активизируются с помощью системных сценариев, которые не запускаются с терминала или уже отсоединились от своих управляющих терминалов. Но можно также запустить процесс в фоновом режиме, выйти из системы, снова войти в нее или же посредством другого терминала найти свой процесс, который все еще выполняется, но не имеет управляющего терминала. Чтобы узнать, к какому терминалу вы подсоединены в настоящее время, воспользуйтесь командой tty. Например: % tty /dev/ttyp2 Команда tty, которая выполняется, когда нет управляющего терминала, выдает сообщение "not a tty" (нет терминала). - TOR 38.07 Почему команда ps выводит некоторые команды в скобках Некоторые версии команды/w, а также ее производные, например команда w, иногда выводят название команды в скобках [в одной из версий UNIX для этого употребляются квадратные скобки — JP\: % ps -f -u jerry UID PID PPID С STIME TTY TIME COMMAND jerry 29240 29235 0 07:56:19 ttypl 0:01 sh find_mh_dupes jerry 29259 29240 23 07:57:52 ttypl 0:07 (egrep) Это можно объяснить только вкусами автора команды. Скобки являются признаком того, что команда перезаписала свое имя, а также того, что команда/и не нашла его и использует вместо него учетное имя. (Учетное имя — это последний компонент имени, переданного системному вызову еХес (3&02), а также имя, используемое в файле учета системных ресурсов.) Команда ps выполняет следующее [на языке С — JP\: if (proc->a_rgv == NULL || strcmp (proc->acct_name, proc->argv[0] ) != 0) printf("(%s)", proc->acct_name); Если список переменных среды имеет большой объем, команда ps не в состоянии получить массив аргументов. Это связано с. тем, что она читает только несколько последних страниц стека каждого процесса. Другие версии "команды ps используют иной механизм определения аргументов команды и, возможно, никогда не будут выводить скобки. — СТ, из телеконференции net.unix-wizards в Usenet, 13 ноября 1983 г. 38.08 Что такое сигналы? Сигналы являются простым и в то же время важным средством связи между процессами. Выражение "связь между процессами" звучит напыщенно, но в действительности обозначает 604 Часть шестая. Управление процессами
38.08 простое понятие — средства, с помощью которых одна программа посылает сообщения другой. Принято считать, что сигналы — это специальные сообщения, посылаемые ядром системы UNIX а.м), но в действительности каждая программа может обмениваться сигналами с любой другой программой. Сообщения скольких видов можно посылать посредством сигналов? Таких разновидностей относительно немного. Сигналы — это не произвольные сообщения, наподобие писем, а небольшая группа предопределенных сообщений, каждое из которых имеет особое значение. System V поддерживает 16 сигналов. Каждому из них присвоен номер. Реализации UNIX, произошедшие от систем BSD и SVR4, имеют 32 сигнала. В табл. 38.2 перечислены некоторые из наиболее распространенных сигналов. В ней также указываются символы клавиатуры, которые порождают сигналы в системе BSD (эти символы могут быть изменены; см. параграф 5.09). Таблица 38.2. Наиболее распространенные сигналы Название сигнала HUP INT QUIT KILL SEGV TERM STOP TSTP com CHLD USR1 Номер 1 2 3 9 11 15 17 18 19 20 30 Значение/функция "Отбой" — завершить выполнение. Посылается при выходе из системы или отключении модема "Прервать" — завершить выполнение. Посылается при нажатии клавиш [CTRL-c] "Выйти" — завершить выполнение (и вывести файл дампа (53.01)). Посылается при нажатии клавиш [CTRL-\] "Уничтожить" — завершить безусловно и немедленно; хороший способ аварийного завершения "Нарушение сегментации" — попытка получить доступ к недоступной области памяти "Завершить" — осуществить нормальное завершение, если имеется такая возможность "Приостановить" — приостановить безусловно и немедленно; продолжить, получив сигнал CONT "Приостановить" — приостановить выполнение и подготовиться к продолжению (либо в фоновом, либо в интерактивном режиме). Посылается при нажатии клавиш [CTRL-z]. Эту команду вызывает команда stty (5М) "Продолжить" — продолжить выполнение после выдачи сигнала STOP или CONT "Порожденный процесс" — сигнализирует об изменении статуса порожденного процесса Сигнал, определенный пользователем Несмотря на то, что список неполный, таблица довольно наглядно демонстрирует, каковы функции сигналов. Многие сигналы, например SEGV, являются предупреждениями или сообщениями об ошибке. Вам, наверное, приходилось сталкиваться с досадным сообщением segmentation violation (нарушение сегментации). Оно выдается, когда ядро обнаруживает какую-то неисправность и посылает программе сигнал SEGV. В ответ на этот сигнал программа завершается. Другие сигналы, такие как TSTP, генерируются в ответ на специальные символы, вводимые с клавиатуры. А многие сигналы просто сообщают: "Ваше время закончилось, до свидания!" Когда процесс получает сигнал, он может предпринять ряд действий, например: • Выполнить действие по умолчанию, которое определено для данного сигнала. Некоторые сигналы по умолчанию вызывают уничтожение процесса, который их получил, а некоторые приводят к остановке процесса и выводу файла дампа. (В качестве примера можно указать сигнал QUIT) Ряд сигналов не требует выполнения каких-либо действий. Запуск, останов и уничтожение процессов 605
38.09 • Перехватить сигнал с помощью команды trap (44.12) и выполнить специальную функцию обработки сигнала. Эта функция часто делает все необходимое для корректного завершения работы программы, гарантируя, что все файлы закрыты и в дальнейшем не возникнут ошибки, связанные с их состоянием. • Проигнорировать сигнал. В этом случае ничего не произойдет. Вы, наверное, читали, что команда kill -9 вызывает безусловное уничтожение процесса. Почему? Два сигнапа из табл. 38.2 не могут быть перехвачены или проигнорированы. Это сигналы KILL и STOP. Команда Ш os.io) (kill — убить. — Примеч. перев.) никого не убивает. Она не делает ничего, кроме того что посылает сигналы. - ML 38.09 Уничтожение интерактивных заданий Вы, вероятно, знаете, что при нажатии клавиш [CTRL-c] завершается выполнение интерактивного задания. А известно ли вам, что происходит на самом деле? Нажимая клавиши [CTRL-c], вы посылаете процессу сигнал INT (прервать) (38.08). Хорошо спроектированные программы перехватывают данный сигнал; т.е. программа запускает специальную функцию ("обработчик сигналов"), которая вызывается каждый раз, когда поступает сигнал. Обычно обработчик сигналов закрывает все открытые файлы, надлежащим образом выполняет переустановку терминала (если в этом есть надобность) и делает все необходимое, чтобы программа могла завершиться корректно. Затем программа завершается. Аналогично действует сигнал QUIT, который направляется при нажатии клавиш [CTRL-\]. Но при этом еще создается файл дампа (53.oi), необходимый для отладки. Конечно, обработчик сигналов выполняет и совершенно другие функции: в программе может быть принято решение не выходить из системы, а выполнить иное действие. Редакторы типа vi или Emacs почти всегда игнорируют большинство сигналов, а в интерпретаторе Bourne shell сигналы обрабатываются командой trap (44.12). Всякий раз, когда вы посылаете сигнал с клавиатуры, он направляется всем процессам одной и той же группы (за.вз). Порожденные процессы могут входить в эту группу, а могут и не входить. И конечно, каждый порожденный процесс может игнорировать сигналы по своему усмотрению. Но чаще всего уничтожение процесса-родителя приводит к уничтожению порожденного процесса. В параграфе 5.09 объясняется, как назначить клавиши для подачи того или иного сигнапа. Команда Ш (жн» тоже посылает сигналы. - ML, JP 38.10 Уничтожение процессов по команде kill Иногда необходимо послать сигнал какому-либо процессу (3s.ii). Для этого, собственно, и предназначена команда kill. Эту команду можно использовать как с идентификатором сигнала, так и без него: % kill pid % kill -сигнал pid где pid — идентификатор процесса, а сигнал — сигнал, который необходимо послать процессу (необязательный параметр). По умолчанию применяется сигнал с номером 15. Это сигнал TERM, указывающий процессу завершить работу. В System V сигнал должен быть задан с помощью номера. В BSD разрешается использовать либо номер сигнала, либо его символическое имя. [Чтобы получить список имен сигналов, введите команду kill -/. К сожалению, в списке не демонстрируется соответствие имен и номеров сигналов. Однако имена выводятся по порядку, так что если вы умеете считать, то сможете с легкостью определить номер. :-) — TOR] 606 Часть шестая. Управление процессами
38.11 Иногда процесс продолжает существовать и после выполнения команды kill. В таком случае следует выполнить команду kill с опцией -Р, в соответствии с которой процессу посылается сигнал 9, имеющий вполне подходящее название — KILL. В большинстве случаев этот сигнал обеспечивает уничтожение процесса. Однако при поступлении этого сигнала завершаемому процессу не позволяется "навести после себя порядок". Следовательно, могут остаться файлы процесса, которые не были закрыты должным образом. Порой процессы не исчезают даже после того, как им был послан сигнал KILL. Подавляющее большинство таких процессов попадает в одну из трех категорий: • Процессы-зомби.* Процесс, который находится в состоянии зомби (за.Н), в выводе команды ps в BSD имеет статус Z, а в System V — статус <defunct> (несуществующий) (3S.05). Когда процесс завершается, он уведомляет своего родителя о приближающейся кончине. После получения подтверждения его PID удаляется из таблицы процессов. Процесс-зомби — это процесс, ресурсы которого полностью освобождены, но завершение которого так и не было подтверждено его родителем. Обычно, если родительского процесса уже нет в системе, вмешивается процесс init. Но иногда и этого не происходит. Процессы-зомби не оказывают негативного влияния на производительность компьютера и всегда уничтожаются при следующей загрузке системы. • Процессы, ожидающие недоступных ресурсов сетевой файловой системы (из). В частности, к этой категории принадлежат процессы, пытающиеся осуществить запись в удаленный файл на компьютере, где произошел аварийный сбой. Такие процессы не уничтожаются при получении сигнала KILL. В подобной ситуации можно воспользоваться сигналом QUIT (номер 3) или INT (номер 2). • Процессы, ожидающие доступа к устройству, чтобы завершить начатую операцию. В большинстве случаев такой процесс ожидает окончания перемотки магнитной ленты. Уничтожение процесса может также привести к уничтожению всех порожденных процессов. Однако порожденные процессы не исчезают, если они блокируют, или "перехватывают", направляемые им сигналы. Хотя, как объяснялось выше, сигнал KILL (идентификатор 9) обычно вызывает завершение и этих процессов. Следовательно, уничтожение интерпретатора shell может привести к уничтожению всех интерактивных процессов, а также к приостановке процессов, выполняемых в фоновом режиме (включая другие интерпретаторы shell), если все они были запущены этим интерпретатором. Уничтожение интерпретатора shell, запущенного при входе в систему, равносильно выполнению процедуры выхода из системы. Это удобный (хотя и несколько неприятный) способ предотвращения некоторых проблем. Например, если пользователь "дезориентировал" редактор, введя неправильные управляющие последовательности или задав бесконечный цикл, из которого невозможно выйти нормальным образом, уничтожение командного интерпретатора пользователя позволит восстановить контроль над ситуацией, возможно, за счет потери части редактируемых данных. Воспользуйтесь командой ps, чтобы определить, какой процесс является интерпретатором shell пользователя. Помните: вы сможете уничтожить чьи-либо процессы только в том случае, если имеете статус суперпользователя (1.24). — AF, из книги Essential System Administration издательства O'Reilly & Associates 38.11 Наблюдение за очередью на печать: перезапускаемый сценарий-демон интерпретатора shell [Может показаться, что тема данного параграфа не перекликается с темой главы. Однако в нем иллюстрируется другой аспект управления сигналами, а именно: что делает программа или сценарий интерпретатора shell во время приема сигнала. В сценарии Джерри используется команда trap (44.12), предназначенная для перехвата различных сигналов. Этот сценарий действует по-разному, в зависимости от того, получен сигнал "отбой" (HUP, или сигнал номер 1) или TERM (сигнал номер 15). — ТОЩ * Зомби — оживший мертвец. Запуск, останов и уничтожение процессов 607
38.11 В UNIX-системах выполняются программы-демоны, такие как сгоп(8) и syslogd(8), которые пребывают в фоновом режиме в ожидании работы. Многие демоны во время запуска читают файлы конфигурации. Системные администраторы иногда меняют эти файлы и хотят, чтобы демоны прочли их повторно. Один из способов состоит в завершении и повторном запуске программы. Это не лучший способ. Кроме того, в данном случае на протяжении нескольких секунд демон бездействует, пока его не перезапустят. Поэтому многие демоны построены так, что могут повторно читать файлы конфигурации и/или перезапускать себя при получении определенного сигнала (обычно — сигнала HUP с номером I). Для этого системные администраторы узнают идентификатор процесса-демона и посылают ему данный сигнал посредством команды kill. Поскольку демон перехватывает этот сигнал, он не уничтожается. Сценарий интерпретатора shell можно выполнять в виде демона, переведя его,в фоновый режим.* В этом параграфе в качестве примера приведен простой сценарий с названием watchq, который читает из файла имена очередей на печать и запоминает их в переменной интерпретатора shell. Каждые 30 секунд он выполняет команду l££ (43.02) для каждой очереди к принтеру, указанной в этом списке. Если в какой-либо очереди содержится ошибка, сценарий с помощью команды write (из) выводит сообщение и выходные данные команды 1р<1 для того пользователя, с которым связана эта очередь. По истечении какого-то времени с момента запуска сценария принтер с именем office прекращает свою работу. Я редактирую файл watchq и удаляю имя этого принтера, чтобы пользователь по имени lisa, которому не повезло с принтером, перестал получать от системы жалобы на работу устройства. Затем я посылаю сценарию сигнал о необходимости прочитать файл повторно: % cat watchq #! /bin/sh # watchq - сценарий-"демон", который наблюдает, нет ли ошибок в очередях на печать temp=/tmp/WATCHQ$$ # Сохранить вывод команды lpq watch=/usr/local/lib/watchqs # Имена очередей, за которыми ведется наблюдение writeto=lisa # Пользователь, получающий уведомления об ошибках # принтера queues=",cat $watch~" # Поместить список имен очередей в переменную # $queues trap 'queues=" 'cat $watch~"' 1 # Присвоить переменной $queues новое значение, # если получен сигнал HUP trap 'rm -f $temp; exit' 0 15 # Удалить временный файл, когда Сценарий # завершается # Выполнять бесконечный цикл (пока сценарий не будет уничтожен) while : do for queue in $queues do lpq -P$queue >$temp if egrep ' (out of paper I error | warning) ' $tertip >/dev/null then echo "PRINTER QUEUE $queue:" | Cat - $temp | write $writeto fi done sleep 30 done % echo office main lobby > /usr/local/lib/watchqs % watchq 6 [1] 4363 * Обычно рекомендуется перенаправлять ввод и вывод (ixoi, 4S.2I) так, чтобы они попадали не на терминал, а, скажем, на системную консоль. В операционных системах и интерпретаторах shell, которые уничтожают фоновые задания при выходе пользователя из системы, следует применять команду nohup (is.is). ш watchq /dcv/nu!l 13.14 -13.13 608 Часть шестая. Управление процессами
38.12 % echo main lobby > /usr/local/lib/watchqs тзам % kill -l 4363 % kill 4363 [1] Exit -48 watchq На практике сценарий watchq можно запускать из системного файла типа /etc/rc.local при перезапуске системы. Пользователь lisa имеет возможность самостоятельно редактировать файл watchq. Имя пользователя, о котором уведомляет команда write, также можно изменять с помощью команды kill -J. Этот сценарий не защищен от неумелого обращения, в связи с чем могут возникать проблемы, которые трудно обнаружить. Например, в некоторых версиях UNIX иногда не работает команда write, если она запущена из демона, у которого нет управляющего терминала (З8.щ. Кроме того, сообщения об ошибках, которые ищет сценарий egrep (27.05), могут охватывать не все ситуации и быть системно-зависимыми. Предложенный нами сценарий — всего лишь пример великолепного метода быстрого написания демонов. - JP 38.12 Уничтожение всех процессов пользователя Во многих системах UNIX команда Ш (З8.ю) интерпретирует специальный идентификатор процесса -1 как команду направить сигнал всем процессам пользователя (всем процессам, имеющим данный идентификатор пользователя), за исключением процесса, посылающего сигнал. Например, команда % kill -term -i вызывает завершение всех процессов пользователя.* Если вы не уверены, что ваша система поддерживает такую возможность, наберите команду man 2 kill (so.oi), чтобы прочитать страницу kill(2) интерактивного руководства. Эту команду можно использовать для отмены выполнения фоновых заданий после выхода из системы. Просто вставьте команду kill -TERM -i в свой файл .logout. Правда, имеется ряд веских аргументов, доказывающих, что поступать так не стоит: в случае использования нескольких терминалов эта команда уничтожит все ваши процессы, если вы выйдете из системы на одном из них. Данная команда окажется полезной при возникновении безнадежной ситуации. Если процессы стали неуправляемыми или какая-то программа заблокировала терминал, можно войти в систему с другого терминала и уничтожить все процессы, чтобы потом не искать в выводе команды ps ps.os), какой процесс стал виновником случившегося. [Сценарий zap. (38l3> просматривает список процессов и автоматически уничтожает их. — JP] Для суперпользователя специальный идентификатор процесса -1 определен отдельно. Для пользователя root этот идентификатор означает: "все процессы, за исключением системных". Если вы не можете использовать идентификатор процесса -1, но работаете в интерпретаторе Bourne shell или в другом интерпретаторе, где отсутствует механизм управления заданиями, указывайте идентификатор процесса 0 (нуль). В таком случае заданный сигнал посылается всем членам группы процессов (т.е. процессам, которые запускались в течение текущего сеанса работы в системе). В интерпретаторах типа С shell, поддерживающих управление заданиями (i2.oi), идентификатор 0 не вызывает никакого действия. - ML, JP, JIK * Сигнал номер 15 — TERM — посылается командой kill по умолчанию. В данном примере его следует указывать явно по вполне очевидным синтаксическим причинам. Запуск, останов и уничтожение процессов 20 9-171 609
<9) zap 38.13 38.13 Интерактивное уничтожение процессов, соответствующих шаблону При необходимости уничтожить процессы не очень удобно выполнять целую цепочку операций: активизировать команду ps (3s.os), определять идентификатор процесса и только потом уничтожать сам процесс. Брайан Керниган и Роберт Пайк в своей классической книге "UNIX — универсальная среда программирования"* приводят сценарий zap. В этом сценарии используется утилита egrep (n.os), предназначенная для выбора уничтожаемых процессов. Можно вводить расширенные выражения, соответствующие нескольким процессам, например: % zap 'troff|fmat' PID TTY TIME CMD 22117 01 0:02 fmat somefile? n 22126 01 0:15 sqtroff -ms somefile? у Воспроизводим этот сценарий с разрешения авторов: #! /bin/sh # zap: уничтожить все процессы, соответствующие шаблону PATH=/bin:/usr/bin IFS=' 1 # просто символ новой строки case $1 in "") echo 'Usage: zap [-2] pattern' 1>S2; exit 1 ;; -*) SIG=S1; shift esac echo ' PID TTY TIME CMD1 \\..y 4S.31 kill $3IG "pick Vps -ag | egrep "$*"\" | awk '{print SD1' Команда ps -ag выводит на экран все имеющиеся в системе процессы. Удалите опцию а, чтобы получить только свои процессы. Возможно, используемая вами версия команды ps требует указания других опций (38.os). Данная версия сценария zap вызывает другой сценарий — pick .** Он приведен ниже. Сценарий pick отображает строку своих аргументов и ожидает, чтобы вы ввели у, q или еще что-то. Если вы введете у, строка будет направлена на стандартный вывод, а если — q, выполнение сценария pick прекратится и следующие строки не попадут на экран. При вводе любого другого ответа на экране отображается следующая строка. Сценарий zap использует утилиту awk (зз.п) для вывода первого аргумента (идентификатор процесса), выбираемого в выходной строке команды ps с помощью сценария pick. В сценарии zap с помощью вложенных (4S.3i) обратных кавычек (9.16) вывод команды ps, отфильтрованный утилитой egrep, передается сценарию pick. Так как в сценарии zap переменной IFS (зз.н) присвоен символ новой строки, сценарий pick выбирает и отображает на экране каждую строку вывода команды ps в виде единого аргумента. Вывод сценария pick, пропущенный через утилиту awk, передается команде Ы[ ра.щ. Если это объяснение кажется вам недостаточно подробным, внимательно просмотрите сценарии — они действительно заслуживают внимания. (Обратитесь также к главам 44—46, в которых говорится о программировании для интерпретатора shell.) Ниже приведен текст сценария pick (на компакт-диске этот сценарий находится в файле zap). #! /bin/sh # pick: выбор аргументов PATH=/bin:/usr/bin for i * Керниган Б. В., Пайк Р. UNIX — универсальная среда программирования: Пер. с англ. — М.: Финансы и статистика, 1992. — 304 с: ил. — Примеч. иерее. ** В системе электронной почты МН также имеется команда pick. Если вы пользуетесь этой системой, присвойте своему сценарию другое имя, например choose. 610 Часть шестая. Управяение процессами
38.15 -n 46.10 /dev/tty/ 45.20 done < 4S.22 do don echo -n "$i? " read response case ^response у ) echo $i q*) break esac e </dev/tty >/dev/tty in ; ; - JP 38.14 Процессы стали неуправляемыми? Пошлите им сигнал STOP Иногда процессы выходят из-под контроля и начинают лавинообразно дублировать себя с помощью системного вызова fork ps.02). Когда вы уничтожите один процесс, будет порождено еще пятьдесят. Как решить подобную проблему? • В системах, поддерживающих управление заданиями (izoi), это можно сделать с помощью сигнала STOP, который приостанавливает процессы. kill 3S. 10 % kill -STOP . . . Приостановите все процессы, которые можно приостановить. В результате порождение новых процессов станет невозможным. После этого начните удалять их, используя команду kill -9. • Если системный администратор ограничил в вашей системе количество процессов для каждого пользователя, можете радоваться: крах системы, вызванный избыточным числом процессов, предотвращен. Но, к сожалению, это имеет и отрицательные стороны. В какой-то момент времени вам не удастся выполнить еще одну команду, которая не встроена в интерпретатор shell, поскольку исчерпан лимит на количество процессов: % рэ No more processes. (Не разрешается создавать процессы.) Если это произойдет, войдите в систему под другим именем или попросите кого-нибудь выполнить команду, которая выдаст список ваших процессов. В зависимости от системы, эта команда может иметь одну из двух форм: % рэ -и ааше_имяг System V % ps aux | grep ааше_имяг BSD После этого вернитесь к своему терминалу и начните останавливать процессы :-). Если вы получите сообщение об ошибке No more processes, значит, ваш интерпретатор не имеет встроенной команды kill. Интерпретатор С shell предоставляет такую команду. Будьте внимательны и наберите следующую команду, чтобы заменить прежний интерпретатор интерпретатором С shell. He сделайте ошибки, так как, скорее всего, не сможете снова войти в систему. схес 45.07 $ exec /bin/csh % kill ... - JP 38.15 Удаление "неуничтожаемых" процессов Как сообщает команда^ (38.05), встречаются процессы, которые находятся в режиме ожидания ввода по нескольку дней. Если вы не можете уничтожить такой процесс даже с помощью команды kill -9 (ЖЮ), значит, в системе имеется ошибка или какая-либо другая проблема. • Эти процессы могут быть неуничтожаемыми, потому что они выполнили запрос к аппаратному устройству или сетевому ресурсу. UNIX перевел их в режим ожидания с Запуск, останов и уничтожение процессов 20» 611
38.16 очень высоким приоритетом, а ожидаемое ими событие так и не наступило (например, из-за проблем в сети). По этой причине до наступления аппаратного события будет приостановлена обработка всех других -сигналов. Сигнал, посланный командой kill, ни к чему не приведет. • Если проблема связана с терминалом и если вам не трудно добраться до разъемов на задней стороне терминала или компьютера, проверьте разъемные соединения. Попробуйте также нажать клавиши [CTRL-q] — это может привести к выгрузке процесса, если перед тем пользователь нажал клавиши [CTRL-s], пытаясь предотвратить вывод большого количества данных на экран. • Поинтересуйтесь у своего поставщика, нет ли специальной команды для переустановки драйвера устройства (42.oi). Если такая команда отсутствует, то, по всей вероятности, вам придется перезагрузить компьютер. - JP 38.16 Почему невозможно уничтожить процессы-зомби? [Процессы, обозначенные в поле статуса вывода команды ps как <exiting> или Z, называются процессами-зомби. — JP] Зомби нельзя убить — он уже мертв. "Что такое зомби? — спрашивает пользователь. — Почему несуществующий процесс находится в системе?" Уничтоженные процессы остаются в системе по двум причинам. Менее значительной является та, что эти процессы предоставляют некий "контекст", необходимый для освобождения дескрипторов открытых файлов (зв.оз) и других ресурсов (памяти, пространства на диске для файлов подкачки и т.д.). Однако обычно все ресурсы освобождаются немедленно. Операционная система различает процессы по идентификаторам, или PID (Process ID). Каждый процесс имеет также связанный с ним идентификатор родительского процесса, или PPID. Значением PPID является PID процесса, который создал данный процесс посредством системного вызова fork (З8.02), или 1 (PID программы intt (З8.02)), если в настоящий момент родительского процесса уже нет в системе. Пока процесс-родитель выполняется, он помнит идентификаторы порожденных процессов. Эти идентификаторы нельзя использовать повторно, пока родитель знает, что порожденные процессы выполняются. Родитель также должен получить байт статуса (44.07) от каждого порожденного процесса. Системный вызов wait занимается поиском порожденного процесса-зомби и включает его в "коллекцию" таких процессов. В то же время он делает доступным для других процессов его PID и возвращает его статус. Программа init(8), используя системный вызов wait, постоянно занимается поиском порожденных процессов-зомби, поэтому, когда родительский процесс завершается, данная программа "собирает" все порожденные им процессы и немедленно завершает их выполнение, игнорируя статус каждого. Следовательно, чтобы избавиться от процесса-зомби, необходимо подождать, когда поступит сообщение о нем от системного вызова wait. Если сообщение "задерживается" или если идентификатор родительского процесса равен 1, то, скорее всего, процесс "застрял" в процедуре закрытия драйвера устройства (42.oi). Это и является главной причиной появления процессов-зомби. Если такое состояние возникает регулярно, значит, драйвер содержит ошибку. — СТ, из телеконференции comp.unix.questions в Usenet, 16 января 1989 г. 38.17 Автоматическое уничтожение фоновых процессов интерпретатором csh при выходе из системы Во многих версиях интерпретатора Bourne shell фоновые процессы (1.26) автоматически удаляются по сигналу HUP (сигнал номер 1), когда пользователь выходит из системы. Однако интерпретатор С shell делает фоновые процессы невосприимчивыми к сигналам, и сигнал 612 Часть шестая. Управление процессами
38.18 /Imp 21.02 !-z 47.04 -v 27.03 cval *./fl set job: if end: rm : tf=/tmp/k$$ s >$tf (! -z $tf) then jobs >$tf.l grep -v Stopped <$tf if (! -z $tf) then eval "echo kill ■ endif if 5tf HUP при выходе пользователя из системы не оказывает никакого воздействия на процессы: они продолжают выполняться. Еслщ^ы хотите, чтобы интерпретатор С shell работал так же, как и Bourne shell, поместите в фэ&я .logout (3.01) следующие команды: #Если есть задания — #получить их список #Отбросить остановленные задания 1 >$tf; rm $tf.l #Канал использовать нельзя. #Если есть выполняющиеся задания -1; sed 's/.\ ( [0-9]*\)-*/%\1/' <$tf~ Только будьте осторожны! В работе интерпретатора csh могут появиться некоторые странности (47.02). [Чтобы увидеть, как это происходит, поместите в начало файла .logout команду set verbose echo (S.i7). Если в процессе выхода из системы очистится экран или закроется окно, введите команду sleep л (4ом) в конец файла .logout, и у вас появится л секунд, чтобы прочитать отладочный вывод. — JP] Обратите внимание на команду jobs >файл, которая выполняется вместо команды jobs | команда, так как последняя вызывает запуск команды jobs в порожденном интерпретаторе shell (звм) и потому не создает никакого вывода. Замечу, что синтаксическая конструкция jobs | встроенная_команда_интерпретато'ра_сзЬ — это хороший повод, чтобы немного посмеяться : -). — СТ, из телеконференции comp.unix.questions в Usenet, 5 августа 1989 г. 38.18 Команда nohup Когда система UNIX только появилась, часто даже локальные терминалы поддерживали связь с ней посредством модемов, действующих на короткие расстояния. (Как-никак, а эта ОС была придумана в телефонной компании.) Если кто-нибудь выходил из системы, модем разрывал телефонное соединение. С другой стороны, если система обнаруживала, что модем отключился, регистрационному интерпретатору посылался сигнал HANGUP, или HUP (отбой), вследствие чего shell завершал свою работу, а также все порожденные им процессы (за.оз). При работе с интерпретатором С shell процессы, которые выполняются в фоновом режиме, невосприимчивы к сигналам HUP, а при работе с интерпретатором Bourne shell такие процессы беспрекословно завершаются при получении данного сигнала. Команда nohup (no hangup — без отбоя) позволяет избежать возникновения такой ситуации. (щ) J (GNU-версия этой команды имеется на компакт-диске.) Для этого достаточно ввести следующее: $ nohup команда & Любой вывод команды, который в обычных условиях направляется на терминал (т.е. не перенаправляется), будет записываться в файл с именем nohup.out, находящийся в текущем каталоге. Конечно, если вы хотите выполнять задания в нерабочее время, лучше воспользоваться командами at, cron и batch (40.01). В некоторых случаях команду nohup удобно применять в сценариях, чтобы дать им указание игнорировать сигналы HUP и TERM (за.оз), хотя команда trap (44.12) более гибкая. (В System V команда nohup заставляет другие команды игнорировать сигналы HUP и QUIT, но не TERM.) - TOR nohup Запуск, останов и уничтожение процессов 613
39 Время и производительность 39.01 Поговорим о времени Когда речь идет о времени в системах типа UNIX, могут подразумеваться два понятия: 1. Собственно время, каким его показывает команда date (Si.io). Оно сохраняется в системе самыми разнообразными способами: как время создания и модификации файла, как время входа в систему и т.д. 2. Продолжительность происходящих событий. В настоящей главе рассматривается время во втором значении. В ней раскрываются такие вопросы: продолжительность выполнения программ, факторы, ускоряющие или замедляющие выполнение программ, что вы можете (или, чаще, не можете) сделать, чтобы повлиять на скорость выполнения. - TOR 39.02 Программы хронометрирования Для проведения хронометрирования предназначены две команды — time и /bin/time. Они предоставляют достаточно точную информацию, поскольку не выполняют никаких действий, которые могли бы вызвать дополнительные задержки. Кроме того, ни одна из этих команд не выполняет анализ подпрограмм и трассировку. Команды выдают общее время выполнения, а также другие статистические сведения. Их можно применять к любой программе. Между командами time и /bin/time есть значительное различие. Команда time встроена в интерпретатор С shell. Следовательно, она не может быть использована, когда вы работаете в интерпретаторе Bourne shell (sh) или применяете сценарии этого интерпретатора. Команда /bin/time представляет собой независимый исполняемый файл и может использоваться более широко. Чтобы выполнить хронометрирование программы, введите одну из команд (time или /bin/time), а затем укажите ту команду, посредством которой вы обычно запускаете нужную вам программу. Например, чтобы определить продолжительность выполнения программы analyze, введите следующую команду: % time analyze inputdata outputfile 9.0u 6.7s 0:30 18% 23+24k 285+148io 625pf+0w Полученный результат показывает, что на программу израсходовано 9 секунд времени работы центрального процессора в режиме пользователя (пользовательское время) и 6.7 секунды в режиме системы (системное время, или время, потраченное на выполнение подпрограмм ядра UNIX, вызываемых из данной программы). В целом выполнение заняло 30 секунд. Полное (или истекшее) время — это время с момента запуска команды до ее завершения, которое показали бы обычные часы. В данном случае учитывается и время ожидания окончания обработки заданий других пользователей, время ввода-вывода и т.д. Полное время превышает общее время использования процессора (иногда даже в несколько раз). Вы можете задать автоматическое хронометрирование программ (не требует указания 614 Часть шестая. Управление процессами
39.03 перед именем программы команды time) или изменить формат вывода результатов, установив соответствующие значения для переменных хронометрирования (39.оз) интерпретатора csh. В приведенном выше примере время использования процессора указано в процентах от полного времени (18%). Остальные данные, предоставленные в отчете, связаны с управлением виртуальной памятью и статистикой ввода-вывода. Эти значения могут быть разными, что зависит от того, какой интерпретатор shell вы используете (за дополнительными разъяснениями обратитесь к документации по интерпретатору csh или к параграфу 39.03). Вернемся к примеру (выполнялся в операционной системе SunOS 4.1.1). В остальных полях указаны такие значения: объем совместно используемой памяти (к), количество операций блочного ввода-вывода (io), количество обращений к страницам памяти, вызвавших ошибки, и количество операций подкачки (pf и w). Во многих реализациях команды time данные показатели неточны, поэтому относитесь к ним критически. Команда /bin/time предоставляет сведения только о реальном (полном), пользовательском и системном времени: % /bin/time analyze inputdata outputfile 60.8 real 11.4 user 4.6 sys [При использовании интерпретатора Bourne shell достаточно набрать команду time. — JP] Вывод этой команды показывает, что программа выполнялась 60,8 секунды, прежде чем завершиться, и что было истрачено 11,4 секунды пользовательского и 4,6 секунды системного времени, что в общей сложности составляет 16 секунд времени процессора. Во многих системах имеется третья команда хронометрирования — timex. Она предоставляет более подробную информацию о времени выполнения, если только в вашей системе разрешен учет процессов. Уточните это на странице руководства timex{\). В параграфе 39.05 полнее раскрыты понятия, которые мы начали рассматривать. — ML, из книги UNIX for FORTRAN Programmers издательства O'Reilly & Associates 39.03 Переменная time интерпретатора С shell Переменная time интерпретатора С shell предназначена для управления встроенной командой time (39.02). Эта переменная позволяет автоматически запускать команду time для всех программ, которые превысили определенный лимит времени использования центрального процессора. Кроме того, с ее помощью осуществляется управление форматом вывода команды time. Начнем с рассмотрения простого примера. Практически в любой UNIX-системе переменную интерпретатора shell (6.os) time можно применять для автоматического запуска команды time, когда программы используют больше процессорного времени, чем разрешено. Установите лимит и присвойте это значение переменной time. Например, чтобы обеспечить автоматический запуск команды time в тех случаях, когда программе требуется более 10 секунд времени процессора, введите следующую команду: % set time=10 % Is filel.ms file2.ms flle3.ms nroff43.13 % nroff -ms *.ma | lpr 4.3u 9.8s 0:23 60% 0+200k 106+103io 143pf+0w Для команды Is не было сгенерировано сообщение, поскольку ее выполнение завершилось прежде, чем истекли 10 секунд. Команде «потребуется около 14,1 секунды, поэтому для нее был создан отчет. Для чего могут понадобиться пользователю эти возможности? Они позволяют автоматически управлять выполнением продолжительных заданий, не копаясь в статистических данных для небольших заданий. Во многих интерпретаторах С shell переменную time можно использовать для придания нужного вида хронометрическим отчетам. Стандартный отчет содержит слишком много информации, из-за чего она не воспринимается. По непонятной причине возможность управлять выводом команды time часто не описывается в документации. Время и произеодитеяьность 615
39.03 Чтобы придать должный вид отчету команды time, введите такую команду: Ч set time=(временной_предел " с!Грока_формата") Обратите внимание: указание временного_предела является обязательным, независимо от того, необходим он вам или нет. Если вы не хотите, чтобы хронометрические отчеты создавались автоматически, установите очень большой временной_предел. Строка_формата может содержать любую комбинацию текста и символов форматирования. В зависимости от символов форматирования, команда time включает в вывод соответствующую информацию. Похоже, что доступные символы меняются от системы к системе (кроме того, эти изменения не всегда документированы, что создает дополнительные трудности). Мы использовали два источника информации: документацию для команды time системы BSD 4.1, написанную Марком Виттенбергом (Mark Wittenberg), и документацию для команды time, которая поставляется с операционной системой Solaris 2.4. Там, где эти две версии различаются, версия Марка обозначена меткой А>, а версия для Solaris — меткой В>. %D A> Средний объем памяти в килобайтах для резидентных страниц памяти с данными и стеком процесса. В> Средний объем памяти в килобайтах для данных. В этом случае данные, используемые совместно, не учитываются. Совместное использование памяти — это сравнительно новое свойство, и для многих программ оно не характерно. %Е Время, необходимое для полного выполнения программы. Вы бы получили это значение, если бы стали с секундомером определять продолжительность выполнения программы. Иногда это время называют "истекшим". %F Количество ошибок при обращении к страницам памяти, т.е. сколько раз UNIX пришлось подгружать страницы виртуальной памяти с диска. Большое число подобных ошибок может служить признаком того, что выполнение программы затягивается по причине нехватки памяти. Чтобы устранить эту проблему, следует нарастить оперативную память компьютера. %1 Число операций блочного ввода, т.е. число обращений программы к диску при необходимости прочесть данные. %К А> Средний объем страничной резидентной памяти для текста, данных и стека процесса, выраженный в килобайтах. В> Средний объем памяти для стека, выраженный в килобайтах. %м Максимальный объем действительной (физической) памяти, которая используется программой во время выполнения, выраженный в килобайтах. (17 октября 1986 г. Дэниел В. Клайн (Daniel V. Klein) сообщил в телеконференции net.unix в Usenet, что в поле вывода, соответствующем параметру %м, на самом деле указывается ровно половина максимального объема памяти. Действительно, это число иногда меньше того значения, что выдается при указании параметра %к. Поэтому Дэниел, вероятно, прав. И как вам после этого недокументированные свойства?) %0 Число операций блочного вывода. %Р Общее время использования центрального процессора, выраженное в процентах от полного времени выполнения программы. Если вы являетесь единственным пользователем в системе и программа выполняет мало операций ввода-вывода, это значение должно быть близким к 100%. Оно уменьшается по мере того, как увеличивается объем вводимых и выводимых данных и повышается общая загруженность системы. %s Системное время использования центрального процессора, выраженное в секундах. Показывает, сколько времени процессор потратил на обслуживание программы в режиме системы, т.е. сколько времени ушло на выполнение системных вызовов программы. %и Пользовательское время использования центрального процессора, выраженное в секундах. Показывает, сколько времени процессор потратил на обслуживание программы в режиме пользователя, т.е. сколько времени ушло на выполнение самой программы. 616 Часть шестая. Управление процессами
39.04 %W Количество операций подкачки. Показывает, сколько раз системе приходилось перемещать всю программу на диск, чтобы освободить память. Если это значение не равно нулю, значит, система нуждается в расширении оперативной памяти. %х А> Средний объем страничной резидентной памяти для текста, выраженный в килобайтах. В> Выраженный в килобайтах средний объем совместно используемой памяти, который требуется программе. Предположим, нам необходимы статистические данные о программах, которым требуется более 10 секунд времени центрального процессора. Кроме того, мы хотим, чтобы в отчете указывалось системное, пользовательское и полное время. Несмотря на большой объем статистических данных, которые пользователь может получить, его интересуют главным образом только эти значения, если, конечно, он не эксперт в области повышения эффективности использования вычислительных систем. Чтобы задать вывод нужных параметров, мы присвоим переменной time строку формата следующего вида (эту строку можно также поместить в файл .cshrc (2.02)): % set time=(10 "System time: %S User time: %U Elapsed time: %E") % nroff -man * > /dev/null System Lime: 0.3 User time: 41.2 Elapsed time: 0:43 Этот отчет более понятен, чем тот невразумительный набор параметров, который вы получали по умолчанию. В нем отчетливо видно, что команде nroff требуется 0,3 секунды времени процессора в режиме системы, 41,2 секунды в режиме пользователя и 43 секунды полного времени. Примечание: Я где-то видел заметку, в которой говорилось, что многие из наиболее непонятных статистических данных команды time в отчете отображаются неправильно. Под "непонятными статистическими данными" я подразумеваю следующие параметры: количество ошибок при обращении к страницам памяти, средний объем памяти для стека и т.п. С доверием можно относиться к значениям системного, пользовательского и полного времени, а также к другим базовым статистическим данным. Если же вас интересуют какие-то особые статистические данные, будьте осторожны. Я сомневаюсь, что какой-либо поставщик устранил эти проблемы. - ML, JP 39.04 Определение среднего времени выполнения команды с помощью сценария runtime Команда time (39.02) определяет время одного прогона команды. Но результаты разных прогонов могут отличаться друг от друга. Сценарий runtime выполняет команду указанное число раз и усредняет результаты. Например: % runtime -5 getdata 0.5 outfile ...подождите немного... runtime summary — 5 runs of % getdata 0.5 outfile (working directory = /users/jerry/.src/getdata) First run started at: Thu Mar 19 09:33:58 EST 1992 Last run finished at: Thu Mar 19 09:36:41 EST 1992 RUN # ***INDIVIDUAL RESULTS*** 0 runtime Время и производитепьность 617
39.05 1 l.Ou 7.4s 1:06 12% 0+108k O+Oio Opf+Ow 2 0.2u 0.8s 0:05 16% 0+128k O+Oio Opf+Ow 3 0.2u 1.3s 0:11 13% 0+116k O+Oio Opf+Ow 4 0.4u 2.7s 0:25 12% 0+108k O+Oio Opf+Ow 5 0.9u 5.9s 0:53 12% 0+108k 0+0io Opf+Ow AVERAGES: 0.54u 3.62s 0:32 0+113k 0+0io Opf+Ow Этот сценарий хорошо подходит для тестирования различных версий программы, когда необходимо найти самую быструю (или самую медленную!) версию. Если вы пишете программу, которую планируете часто использовать, уменьшение времени ее выполнения на 10 или 20% стоит того, чтобы над этим поработать. Учтите, что выполняемая команда не должна производить перенаправление ввода-вывода. Причина заключается в том, что сценарий runtime в ряде случаев сам выполняет перенаправление. Правда, вы можете перенаправить вывод сценария runtime в файл и получить все статистические данные в фоновом режиме. Например: % runtime -5 getdata 0.5 outfile > runtime.out & [1J 12233 Результаты работы сценария будут записаны в файл runtime.out. - JP 39.05 Почему система работает так медленно? Для пользователя производительность означает время, необходимое для выполнения его задания. Системный администратор относится к этому просто: задание пользователя может выполняться долго, потому что оно плохо написано или нерационально использует ресурсы компьютера. К тому же, системный администратор должен оптимизировать производительность выполнения программ для всех пользователей системы, что намного сложнее, чем для одного пользователя. Далее рассматриваются факторы, влияющие на производительность. Утилита /bin/time (39.02) выдает информацию о том, сколько времени требуется для выполнения программы, разбивая общее время на отдельные значимые компоненты. В качестве примера рассмотрим следующий отчет о времени выполнения: % /bin/time application 4.8 real 0.5 user 0.7 sys Как следует из отчета, программа выполнялась приблизительно 4,8 секунды. Это полное, или истекшее, время, т.е. то время, которое показал бы секундомер. В действительности на выполнение программы система потратила гораздо меньше времени. Она израсходовала 0,5 секунды пользовательского времени (столько времени ушло на выполнение кода программы в режиме пользователя) и приблизительно 0,7 секунды системного времени (столько времени ушло на выполнение системного кода UNIX). В общей сложности израсходованное время процессора (действительное время выполнения программы) составляет только 1,2 секунды, или 1/4 полного времени.* На что же уходит остальное время? Часть времени тратится на выполнение операций ввода-вывода (текста), что не отображается командой /bin/time. Управление операциями ввода-вывода требует некоторых вычислений, затраты на которые относятся на счет системного времени. Время, которое тратится дисковыми драйверами, сетевыми интерфейсами, контроллерами терминалов и другим аппаратным обеспечением, не учитывается. * Обратите внимание: хотя версии команды /bin/lime в BSD и System V имеют разный формат вывода, они предоставляют одну и ту же информацию. Кроме того, команда /Ын/tinie отличается от команды time интерпретатора С shell (З1.аз), которая выдает более подробный отчет. 618 Часть шестая. Управление процессами
39.05 Большая часть полного времени уходит на выполнение заданий других пользователей, что вызывает дополнительные издержки (время переключения контекста, время подкачки и т.д.). Общее время выполнения программы зависит от многих факторов. Если вы понимаете роль этих факторов, то сможете понять и всю проблему целиком. Ниже дается перечень всех этих факторов. • Время работы процессора в режиме пользователя. Действительное время, которое центральный процессор тратит на выполнение программы. Здесь время, затрачиваемое на выполнение библиотечных функций, учитывается, а время, уходящее на выполнение системных вызовов (т.е. время, в течение которого ядро UNIX выполняет процесс пользователя), — нет. Программисты могут контролировать время работы процессора в режиме пользователя, зная, какие библиотечные функции являются эффективными, а какие — нет. Необходимо также уметь пользоваться программами трассировки, позволяющими выявлять самые продолжительные по времени места программы. • Время работы процессора в режиме системы. Время, которое центральный процессор тратит на выполнение кода ядра системы. В данном случае учитывается время выполнения системных вызовов и административных функций, связанных с программой пользователя. Между временем, затраченным на выполнение простых библиотечных функций, и временем, затраченным на обслуживание системных вызовов, имеется большая разница, хотя многие этого не понимают. Вызов функции slrcpy, которая копирует символьную строку, полностью выполняется в режиме пользователя, поскольку для вызова не требуется никакой специальной обработки в ядре. Вызовы функций printf, fork намного сложнее. Эти функции содержат запросы на обслуживание ядром UNIX, поэтому какое-то время (относительно небольшое) они выполняются в режиме системы. Все подпрограммы ввода-вывода требуют обслуживания ядром. Программисты в некоторой степени могут влиять на время работы центрального процессора в режиме системы. Хотя нельзя изменить продолжительность обслуживания какого-либо системного вызова, зато можно писать программы так, чтобы системные вызовы выполнялись более эффективно (например, чтобы во время ввода-вывода данные пересылались блоками большего объема). • Время операций ввода-вывода. Время, затрачиваемое подсистемой ввода-вывода на обслуживание запросов ввода-вывода, возникающих в программе. В UNIX это время трудно измерить, однако существуют средства, определяющие, перегружена ли подсистема ввода-вывода, а также методы изменения ее конфигурации, которые позволяют смягчить проблемы, связанные с загруженностью. • Время работы в сети. Время, которое подсистема ввода-вывода тратит на обслуживание сетевых запросов, выдаваемых в программе. В действительности работа в сети является разновидностью операций ввода-вывода, и время на ее выполнение существенно зависит от конфигурации сети и ее загруженности. • Время, затраченное на выполнение других программ. По мере увеличения загруженности системы центральный процессор все меньше времени тратит на обработку одного задания. В результате увеличивается полное время, требуемое для выполнения задания. Это неприятное явление, но поделать тут ничего нельзя, за исключением того, что можно решить некоторые проблемы, связанные с вводом-выводом и управлением виртуальной памятью. • Эффективность использования виртуальной памяти. Это самый сложный аспект производительности системы. В идеале все активные задания должны постоянно находиться в оперативной памяти. Но когда оперативная память полностью занята, система начинает перемещать часть заданий на диск, освобождая таким образом память для тех заданий, которые необходимо выполнить в первую очередь. На это уходит время. Время требуется также для того, чтобы переместить назад, в оперативную память, и продолжить выполнять задания, помещенные на диск. При выполнении заданий с непомерными требованиями к памяти производительность системы может значительно снизиться. Если вы имеете дело преимущественно со стандартными утилитами и коммерческими приложениями, то никак не сможете повлиять на пользовательское или системное время использования процессора, поскольку для этого необходимо переписывать программу. Однако Время и производительность 619
39.06 за счет изменения некоторых настроек системы можно повысить эффективность использования виртуальной памяти и устройств ввода-вывода. Кроме того, существуют способы повышения эффективности работы больших приложений. С точки зрения пользователей, важным показателем является время реакции клавиатуры, хотя оно никак не влияет на время выполнения программы. Если между нажатием клавиши и выводом на экран соответствующего символа наблюдается значительный промежуток, пользователь думает, что система начала работать менее производительно, не обращая внимания на то, сколько времени занимает выполнение задания. Чтобы предотвратить переполнение буферов терминала и, следовательно, потерю символов, большинство UNIX- систем назначают драйверам терминалов (42.oi) очень высокий приоритет. В результате время реакции клавиатуры может оказаться неудовлетворительным, но только при исключительно высокой загруженности системы. При доступе к удаленной системе по сети плохая реакция на нажатия клавиш может быть обусловлена задержками в сети. Производительность работы сети — еще одна очень сложная тема. — ML, из книги System Performance Tuning издательства O'Reilly & Associates 39.06 Команда lastcomm: какие команды сейчас выполняются и как долго они будут выполняться? Если вы отлаживаете программу, пытаясь выяснить, почему она расходует так много времени процессора, или хотите узнать, какие команды кто-то (в том числе, и вы сами) выполняет в системе, вам может помочь имеющаяся в Berkeley UNIX команда lastcomm (при условии, что в системе разрешен учет процессов). В следующем примере приведен список команд пользователя lesleys: % data Mon Sep 4 16:38:13 EDT 1995 % lastcomm lesleys emacs lesleys ttypl 1:41 sees Wed Sep 4 16:28 cat X lesleys ttypl 0:06 sees Wed Sep 4 16:37 stty lesleys ttypa 0:02 sees Wed Sep 4 16:36 tset lesleys ttypa 0:12 sees Wed Sep 4 16:36 sed lesleys ttypa 0:02 sees Wed Sep 4 16:36 ■hostname lesleys ttypa 0:00 sees Wed Sep 4 16:36 quota lesleys ttypa 0:16 sees Wed Sep 4 16:35 Процессы упорядочены по времени завершения: начинается список с процессов, закончившихся совсем недавно. Процесс emacs был запущен десять минут назад с терминала ttypl (з.щ. На его выполнение ушло 1,41 секунды времени процессора. В какой-то момент времени, когда процесс emacs еще выполнялся на терминале ttypl, пользователь lesleys запустил команду cat, а потом прервал ее (на это указывает отметка х). Поскольку процесс emacs выполнялся на том же терминале, что и процесс cat, но закончился позже, Лесли должна была приостановить процесс emacs (используя клавиши [ClKL-z]) (izoi), чтобы запустить процесс cat. Процессы, связанные с терминалом ttypa, запущены из файлов .cshrc и .login пользователя lesleys (хотя это невозможно определить по результатам вывода команды lastcomm). В этом листинге не указан командный интерпретатор (csh), запущенный при входе в систему с терминала ttypa, потому что его работа еще не прекращена. Он появится в выводе команды lastcomm после того, как пользователь lesleys выйдет из системы на терминале ttypa. Это далеко не все, что может делать команда lastcomm (см. документацию). Небольшая подсказка: в очень загруженной системе, в которой зарегистрировано много пользователей и выполняется много программ, команда lastcomm работает медленно. Можно перенаправить ее вывод по каналу другой команде или в файл, например: % lastcomm lesleys > lesley.cmds & % cat lesley.cmds ...никакого ответа... tee 13.09 % lastcomm lesleys | tee lesley.cmds ...никакого ответа... 620 Часть шестая. Упрааланиа процессами
39.07 В результате команда lastcomm будет записывать данные в файл или в канал не построчно, а большими блоками. Поэтому вам может казаться, будто ничего не происходит. Если терминал необходимо выключить во время выполнения команды lastcomm, то существует два метода сохранения вывода этой команды. Если оконная система или эмулятор терминала ■ имеет режим "log to file", воспользуйтесь этим режимом, пока выполняется утилита lastcomm. В противном случае сначала запустите программу script (Si.os), чтобы направить вывод в файл, а затем выполните команду lastcomm: % script lesley.cmds Script started, file is lesley.cmds % lastcomm lesleys emacs lesleys ttypl 1:41 sees Wed Sep 4 16:28 cat X lesleys ttypl 0:06 sees Wed Sep 4 16:37 % exit Script done, file is lesley.cmds % И последнее замечание. Утилита lastcomm не может вывести информацию о командах, встроенных в интерпретатор shell о.щ. Время работы этих команд рассматривается как часть времени выполнения интерпретатора. После завершения работы интерпретатора shell вы найдете эти команды в выходных данных команды lastcomm: они будут указаны в строках с именами процессов csh, sh и т.д. - JP 39.07 Проверка загруженности системы: команда uptime Команда uptime системы BSD, которая доступна также в System V Release 4, AIX и некоторых реализациях System V Release 3, дает приблизительную оценку загруженности системы: % uptime 3:24pm up 2 days, 2:41, 16 users, load average: 1.90, 1.43, 1.33 Команда uptime предоставляет следующие данные: текущее время, время, прошедшее с момента запуска системы, и, наконец, три значения, определяющие среднюю загруженность системы, — среднее число процессов, которые были активны на протяжении последней минуты, последних пяти минут и последних пятнадцати минут. Средняя загруженность — это приблизительный показатель использования центрального процессора. Высокие значения означают, что система используется интенсивно и время ее реакции велико. Обратите внимание, что при определении средней загруженности не учитываются приоритеты и значение параметра nice (Зя.оя) процессов, которые выполняются. Какие значения считаются высокими? Как правило, это зависит от системы. В идеале среднее значение загруженности не должно превышать 3, но это не всегда возможно из-за специфики некоторых систем. Более высокие средние значения загруженности допустимы главным образом на машинах с несколькими процессорами. В конечном счете, определение "высокие" относится к таким значениям загруженности, при которых можно не обращаться к команде uptime, чтобы узнать, перегружена система или нет, — это и так несложно определить по времени реакции системы. Кроме того, различные системы ведут себя по-разному при одинаковой средней загруженности. Например, на некоторых рабочих станциях одновременное выполнение одного ресурсоемкого фонового задания и системы X Window (ui) может привести к тому, что реакция операционной системы станет чрезвычайно медленной, хотя среднее значение загруженности останется достаточно низким. В конце концов, на средние значения загруженности стоит обращать внимание только тогда, когда они отличаются от показателей, которые в вашей системе считаются "нормальными". — AF, из книги Essential System Administration издательства O'Reilly & Associates Время и производительность 621
39.08 39.08 Замедление работы программы в "раздутой" среде При запуске нового порожденного процесса создается копия среды процесса-родителя. В некоторых системах, особенно загруженных, создание новых процессов происходит не слишком быстро. (В начале 80-х я работай на компьютере VAX 11/750, где была установлена операционная система BSD 41. На этом компьютере средняя загруженность временами превышала 40. Когда выполнение команды завершалось, следующего приглашения командной строки временами приходилось ждать от 10 до 20 секунд!) Конечно, во многих случаях удобно иметь в распоряжении большое количество переменных среды (созданных с помощью команды setenv интерпретатора csh или команды export интерпретатора sh). Однако в результате может существенно замедлиться выполнение заданий, особенно сценариев, запускающих в цикле множество подпроцессов. Как-то поздно вечером, когда я был единственным пользователем, зарегистрированным в системе, я выполнил один тест на нашем 386-м компьютере с операционной системой Interactive UNIX System V/386 Release 3.2. Сначала я немного "очистил" среду, сделав ее размер равным приблизительно 300 символам, а затем задал выполнение следующих команд: env 6.01 wc 29.06 time. 39.03 repent 9.25 % % % 0 0. 0 env | wc - 335 set time repeat 50 ,0u 0.1s 0; .lu 0.1s 0: .0u 0.2s 0: -c /bin/true :00 15% :00 18% :00 20% В этом примере 50 раз запускается небольшой сценарий /bin/true. Я просуммировал значения системного времени (вторая колонка) и получил 6,9 секунды. Затем я ввел цикл while интерпретатора С shell, чтобы создать группу строковых переменных большой длины с именами FOOl, F002 и т.д.: set 6.08 % set n = О % while ($n < 30) @ 47.04 ? @ n++ ? setenv FOO$n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx... ? end % env | wc -c 4934 После этого я снова выполнил команду repeat 50 /bin/true. В ситуации, когда размер среды достиг 5000 символов, на выполнение команды ушло 8,9 секунды, что на 30% больше, чем в предыдущем примере. Идеальный тест? Нет, конечно! Но если ваша программа выполняется на медленном компьютере в "раздутой" среде, целесообразно провести подобный тест, чтобы определить, не следует ли основательно "почистить" среду (например, заменить переменные среды переменными интерпретатора shell для ускорения запуска подпроцессов). В параграфе 38.07 рассматривается, с какими проблемами может быть связана работа команды ps в "раздутой" среде. Параграф 2.09 содержит инструкции по ускорению запуска интерпретатора С shell. - JP 39.09 Знай, когда нужно быть уступчивым по отношению к другим пользователям, а когда нет (Ш) 1 Команда nice изменяет приоритет процессов, выполняемых в режиме разделения времени (т.е. всех процессов, выполняемых в системах BSD и в версиях System V до Release 4). GNU-версию этой команды вы найдете на компакт-диске (утилита инсталляции, имеющаяся 622 Часть шестая. Упраепение процессами
39.09 на диске, устанавливает команду nice только в том случае, если ваша операционная система поддерживает соответствующие возможности). Если вы не знакомы с нюансами работы UNIX, способ определения приоритетов в этой ОС покажется вам достаточно странным — полной противоположностью тому, чего вы ожидали. Процесс с высоким значением параметра nice выполняется с низким приоритетом, удостаиваясь незначительного внимания со стороны процессора. И наоборот, задания с низким значением параметра nice выполняются с высоким приоритетом. По этой причине параметр nice обычно называют фактором уступчивости. Слишком "уступчивое" задание очень "любезно" по отношению к другим пользователям системы (т.е. выполняется с низким приоритетом), в то время как "неуступчивое" задание "захватывает" центральный процессор. Термин "уступчивость" неуклюж в такой же мере, как и сама система приоритетов. К сожалению, это единственный термин, который одновременно является точным и позволяет избежать ужасных парафраз ("увеличение приоритета означает снижение приоритета..."). Многие пользователи, считающие себя опытными, заявляют, что команда nice ни на что не влияет. Не слушайте их. Как правило, снижение приоритета заданий, связанных с вводом- выводом, не оказывает значительного влияния на то, что происходит в системе. Система "вознаграждает" повышением приоритета те задания, которые большую часть времени тратят на ожидание ввода-вывода. Однако снижение приоритета процессов, связанных с центральным процессором, может дать желаемый эффект. Компиляторы, программы пакетной обработки текста (troff, ТЕХ и т.д.), прикладные программы, выполняющие множество математических вычислений, — вот главные кандидаты на обработку командой nice. Я обнаружил, что в умеренно загруженной системе команда nice приблизительно на 30% снижает темп выполнения заданий, интенсивно использующих центральный процессор, а освободившееся время распределяет между заданиями с более высоким приоритетом. Часто можно существенно улучшить реакцию на нажатия клавиш, если выполнять с более низким приоритетом задания, которые интенсивно используют центральный процессор. Отметим, что System V Release 4 имеет более сложную систему приоритетов, включающую также приоритеты реального времени. Для управления приоритетами предназначена команда priocntl. Более ранняя версия команды nice присутствует в системе для обеспечения совместимости. Другие реализации UNIX (включая HP и Concunent) поддерживают управление заданиями в реальном времени. Эти системы имеют собственные средства для управления планировщиком процессов. Команда nice устанавливает значение уступчивости задания, которое используется для определения приоритета задания. Возможно, это одна из наименее унифицированных команд, созданных человеком. Данная команда имеет четыре версии, каждая из которых немного отличается от других. В BSD UNIX существует две версии команды nice, одна из которых встроена в интерпретатор С shell, а другая, самостоятельная, может использоваться с другими интерпретаторами. В System V наблюдается та же картина: команда nice имеет две версии — одна встроена в интерпретатор С shell, а другая является самостоятельной. Те, кто работает в BSD UNIX, должны знать о команде renice (8) (39.11). Эта команда позволяет изменить значение параметра nice после того, как задание запущено на выполнение. System V не предоставляет такой возможности, поэтому в ней нет эквивалента команды renice. Примечание: Хорошенько подумайте, прежде чем применить команду nice к интерактивному заданию, например к текстовому редактору (см. параграф 39.10). Далее мы кратко расскажем о различных версиях команды nice. Команда nice интерпретора С shell в BSD В BSD UNIX параметр nice может иметь значения в диапазоне от -20 до 20. Значение -20 соответствует самому высокому приоритету, 20 — самому низкому. По умолчанию заданиям, Время и производительность 623
39.09 выполняемым пользователем, UNIX присваивает значение nice, равное 0. Наиболее низкие значения параметра (от -17 до -20) неофициально зарезервированы для системных процессов. Присвоение этих значений заданию пользователя может вызвать проблемы. Пользователь всегда может увеличить значение параметра nice для своих заданий (т.е. понизить их приоритет). Повышать приоритет задания вправе только суперпользователь. Чтобы повысить уступчивость задания, укажите перед его именем команду nice. Например, команда % nice awk -f proc.awk datafile > awk.out запускает утилиту awk с низким приоритетом. По умолчанию версия команды nice интерпретатора csh присваивает заданию значение nice, равное 4. Чтобы установить произвольное значение, задайте команду nice одним из следующих способов: % nice +п команда % nice -л команда где л — это целое число от 0 до 20; опция +п — это запрос на присвоение положительного значения параметру nice (низкий приоритет); а опция -л — запрос на присвоение отрицательного значения (высокий приоритет). Только суперпользователь вправе присваивать отрицательные значения параметру nice. Самостоятельная версия команды nice в BSD Самостоятельная версия команды nice отличается от версии, используемой в интерпретаторе С shell, тем, что является отдельной программой, а не встроенной командой интерпретатора. Поэтому сфера применения данной версии гораздо шире: с ней можно работать в /яд^е-файлах (2S.U), при выполнении интерпретатора Bourne shell и т.д. Принципы работы с самостоятельной версией команды nice те же. Параметр nice может принимать значения от -20 до 20. По умолчанию присваивается значение 0. Изменен только синтаксис команды, что иногда приводит пользователей в замешательство. При использовании самостоятельной версии команды nice опция -п задает присвоение положительного значения (низкий приоритет), а опция --п — отрицательного (высокий приоритет; такие значения может устанавливать только суперпользователь). Рассмотрим следующие команды: % nice -6 awk -f proc.awk datafile > awk.out # nice —6 awk -f proc.awk datafile > awk.out Первая команда задает выполнение утилиты awk с высоким значением параметра nice (6). С помощью второй команды, которую может вводить только суперпользователь, утилита awk запускается с низким значением nice (-6). Если значение nice не указано, используется значение по умолчанию — 10. Команда nice интерпретатора С shell в System V В System V значения nice интерпретируются несколько иначе. В качестве аргументов команды nice можно указывать числа от 0 до 39, значением по умолчанию является 0. Хотя в данном случае используется другой диапазон, смысл значений остался прежним: самое высокое значение, 39, соответствует самому низкому приоритету и наоборот. В некоторых реализациях System V команда nice позволяет устанавливать режим выполнения в реальном времени. Задания, для которых пользователь root установил очень низкое значение nice (-20 и ниже), получают все время центрального процессора. Однако систем, в которых эта возможность реализована должным образом, совсем немного, и обычно они рекламируются как системы обработки в реальном времени. В любом случае выполнение заданий подобным образом делает невозможной работу в многопользовательском режиме. Это в корне отличается от системы приоритетов реального времени в System V Release 4. В остальном команда nice интерпретатора С shell в System V ничем не отличается от родственной ей команды в BSD. Чтобы назначить заданию низкий приоритет, используйте следующую команду: % nice комаяда 624 Часть шестая. Управление процессами
39:11 При этом значение nice увеличивается на стандартную величину — 4 (как и в BSD UNIX), и команда выполняется со значением nice, равным 24. Чтобы запустить задание с произвольным приоритетом, воспользуйтесь одной из следующих команд: % nice +п команда % nice -л команда где п — целое число от 0 до 19. Опция +л служит указанием присвоить заданию более высокое значение nice (сниженный приоритет), а опция -п — более низкое значение (повышенный приоритет). Эта команда похожа на одноименную команду в BSD UNIX, за исключением того, что параметр л теперь может быть задан относительно значения по умолчанию. Таким образом, команда % nice +6 awk -f proc.awk datafile > awk.out запустит на выполнение утилиту awk со значением nice, равным 26. Самостоятельная версия команды nice в System V Как и в BSD, самостоятельная версия команды nice в System V полезна при создании /яяАге-файлов, сценариев интерпретатора shell, а также когда в качестве интерпретатора используется Bourne shell. Между указанной версией и аналогичной командой интерпретатора С shell существуют следующие различия: • Самостоятельная версия команды nice, заданная без аргументов, увеличивает значение nice на 10, а не на 4. В этом случае приоритет программы снижается гораздо больше. • При задании опции -п команда nice увеличивает значение nice на величину п (снижая при этом приоритет). • При задании опции --п команда nice уменьшает значение nice на величину п (повышая при этом приоритет; данную операцию может выполнять только суперпользователь). Рассмотрим следующие команды: % nice -6 awk -f proc.awk datafile > awk.out # nice —6 awk -f proc.awk datafile > awk.out Первая команда запускает на выполнение утилиту awk с более высоким значением nice — 26 (соответствует более низкому приоритету). Вторая команда запускает на выполнение утилиту awk с более низким значением nice (14). — ML, из книги System Performance Tuning издательства O'Reilly & Associates 39.10 Ошибка при работе с командой nice Применять команду nice к интерактивным заданиям (Ш1) — НЕ лучшая идея. Если система перегружена, ваш терминал может "зависнуть", когда ему будет выделено достаточно времени центрального процессора. Может даже случиться, что в очень загруженной системе вы не сможете с помощью команды kill (38.09) удалить задание, к которому применена команда nice, т.к. система никогда не предоставит ему столько времени центрального процессора, чтобы можно было распознать посланный заданию сигнал! И конечно, не стоит применять команду nice к таким интерактивным программам, как текстовые редакторы, если только вы не любите ждать... - JP 39.11 Изменение приоритета задания в BSD UNIX Если задание выполняется, то с помощью команды renice(S) можно изменить его приоритет: % /etc/renice приоритет -р id % /etc/renice приоритет -g группа % /etc/renice приоритет -и имя Время и производительность 625
39.12 где приоритет — это новое значение параметра nice, которое должно быть целым числом от -20 до 20. Параметр id — это идентификатор процесса (за.оз), приоритет которого вы хотите изменить. (Идентификатор процесса можно узнать с помощью команды ps (зв.05).) Параметр группа — это номер группы процесса (за.вз), который выводится с помощью команды ps -/; данная версия команды renice модифицирует приоритет всех процессов группы. Параметр имя — это имя пользователя, которое фигурирует в файле /etc/passwd; данная версия команды renice модифицирует приоритет всех заданий, принадлежащих указанному пользователю. Самым высоким значением параметра nice может быть 19: данный процесс будет выполняться в системе только в том случае, если никакое другое задание не претендует на центральный процессор. Если в команде renice задано отрицательное значение, процесс получит больший процент времени центрального процессора, чем если бы его параметру nice было присвоено значение по умолчанию (0). Опять-таки, только суперпользователь вправе понгокать это значение (повышать приоритет процесса). Пользователи могут только увеличивать значение nice (снижать приоритет процесса) и вообще модифицировать приоритеты только тех заданий, которые ими же и запущены. BSD UNIX автоматически повышает значение nice после того, как задания использовали определенное количество времени центрального процессора. Это приводит к косвенному повышению приоритета заданий, которые долгое время не выполнялись. Таким образом, пользователи, выполняющие множество небольших заданий, имеют преимущества перед пользователями, выполняющими несколько длительных заданий. В каждой системе установлено свое предельное время использования центрального процессора, по истечении которого приоритет задания модифицируется автоматически. Обычно это значение равно десяти минутам. - ML 39.12 Почему компьютер работает медленно и как это исправить В параграфе 39.05 рассматривались различные компоненты, которые, с точки зрения пользователей, влияют на скорость работы системы. Существует и другой не менее важный подход к этому вопросу: взгляд на производительность со стороны компьютера. Все спорные вопросы, касающиеся производительности системы, сводятся главным образом к вопросам распределения ресурсов. В любой вычислительной системе существует три главных ресурса: центральный процессор, память и подсистема ввода-вывода (например, локальные и сетевые диски). С этой позиции настройка производительности работы системы означает обеспечение таких условий, при которых каждый пользователь получает свою законную долю доступных ресурсов. Использование ресурсов каждого типа сопряжено с возникновением ряда специфических проблем. Эти проблемы усложняются еще и тем, что все ресурсы взаимосвязаны. Следует тщательно рассмотреть, какую функцию выполняет каждый ресурс: центральный процессор, подсистема ввода-вывода и память. Ниже приведены краткие описания ресурсов каждого типа, а также рассмотрены проблемы, связанные с их использованием. Центральный процессор В любой системе с разделением времени, даже в однопользовательской (например, в UNIX на персональном компьютере), многие программы хотят использовать центральный процессор в одно и то же время. В большинстве случаев ядро UNIX способно "справедливо" распределять ресурсы центрального процессора. Однако в какой-то момент времени центральный процессор теряет способность выполнять возложенную на него работу. Есть несколько способов, позволяющих оценить уровень соперничества за право обладания центральным процессором. Самый простой — вычислить среднее значение загруженности системы с помощью команды uptime (ЗШ) в BSD. В System V информацию подобного рода предоставляет команда sar -q. Для получения этого значения определяется число активных процессов в произвольный момент времени {процесс — это отдельный поток команд). Прежде чем обвинять центральный процессор в недостаточной производительности, давайте проанализируем, что упускается из виду при состязании за обладание процессором. Обычно 626 Часть шестая. Управление процессами
39.12 не учитывается то, что в системе недостаточно памяти или подсистема ввода-вывода не обеспечивает надлежащей скорости работы. Каждая из этих ситуаций может быть причиной значительного замедления работы системы. Процессор же может простаивать большую часть времени, поэтому нельзя сделать вывод о том, что вам необходим более быстрый процессор, основываясь лишь на среднем значении загруженности. Необходимо также понимать, как функционируют подсистемы памяти и ввода-вывода. Часто пользователи приписывают всю вину центральному процессору, но я готов биться об заклад, что в большинстве случаев в такой же мере (если не в большей) следует винить два других ресурса. При нехватке ресурсов центрального процессора можно прибегнуть к одной из следующих мер: • убедите сотрудников выполнять задания ночью или в такое время, когда машина мало используется, что реализуется с помощью команд batch и at (40.01) (таким образом вы добьетесь того, что компьютер будет выполнять полезную работу на протяжении всех 24 часов); • следите за тем, чтобы в системе не выполнялись ненужные задания; • заставьте пользователей выполнять длительные задания с низким приоритетом (39М). Если ни одна мера не поможет, модернизируйте систему. Подсистема памяти Соперничество за право обладания памятью возникает, когда объем памяти, запрашиваемой активными процессами, превышает доступный объем физической памяти системы. С этого момента системе начинает недоставать памяти. Чтобы справиться с данной проблемой, не допустив полного отказа и не удаляя процессы, система начинает выполнять выгрузку страниц (paging) — перемещение блоков памяти, занятых активными процессами, на диск с целью освобождения физической памяти. При этом производительность системы существенно ухудшается. Выгрузка страниц — это не то же самое, что свопинг (swapping), при котором на диск перемещается вся выделенная процессу память. Выгрузка страниц и свопинг указывают, что система не может предоставить достаточный объем памяти процессам, которые в данный момент выполняются, хотя иногда свопинг является обычной мерой поддержания работоспособности системы. В BSD UNIX такие утилиты, как vmstat и pstat, показывают, производит ли система выгрузку страниц, а команда ps сообщает о запросах к памяти, выполняемых каждым процессом. В System V имеется утилита sar, которая предоставляет информацию практически обо всех аспектах использования памяти. Чтобы предотвратить выгрузку страниц, необходимо либо сделать доступным больший объем памяти, либо уменьшить область памяти, за которую состязаются задания. Это потребует настройки системных параметров, что выходит за рамки тем, охваченных в данной книге. Можно также снять (З8.ю) задания, которые запрашивают наибольший объем памяти. Если в системе достаточно памяти, количество запросов на освобождение памяти, которые выдает ядро системы, будет относительно незначительным. Типичными соперниками за право обладания памятью являются большие прикладные программы. Подсистема ввода-вывода Подсистема ввода-вывода является наиболее распространенным источником проблем, связанных с соперничеством за право обладания ресурсами. Пропускная способность устройств ввода-вывода ограничена и распределяется между всеми программами, выполняемыми в данный момент. Шины ввода-вывода способны обеспечивать передачу только определенного количества мегабайт данных в секунду. Пропускная способность отдельных устройств еще ниже. Устройство каждого типа имеет свои особенности, следовательно, с ним связаны какие-то специфичные проблемы. К сожалению, у системы UNIX недостаточно средств для исследования подсистемы ввода-вывода. В BSD UNIX команда iostat позволяет получить информацию о скорости обмена с каждым дисковым накопителем, а команды ps и vmstat — о количестве заблокированных процессов, ожидающих завершения ввода-вывода. В этой же системе команды netstat и nfsstat предоставляют различные статистические данные относительно работы сети. В System V утилита sar подробно информирует о производительности подсистемы ввода-вывода, а команда sadp (V.4) дает все необходимые сведения об обращениях Время и производительность 627
39.12 к диску. Однако отсутствуют стандартные средства, позволяющие оценить скорость реакции подсистемы ввода-вывода в случае ее сильной загруженности. Важный вклад в общую производительность вносят дисковые и сетевые подсистемы. Пропускная способность дисковой подсистемы может быть выражена либо как скорость передачи данных для одного процесса, либо как общая скорость передачи данных. В первом случае определяется максимальная скорость, с которой отдельная программа может читать или записывать данные. Во втором случае определяется максимальная суммарная пропускная способность, которую система может обеспечить для всех программ, выполняемых в настоящий момент. Основные проблемы при сетевом вводе-выводе связаны с перегрузкой сети и с нарушением целостности данных в ней. Проблемы первого типа возникают, когда объем данных, которые необходимо переслать по сети, превышает ее пропускную способность. В этом случае действительная скорость передачи данных для любой задачи относительно невелика. Обычно проблемы загруженности решают путем изменения конфигурации' сети. Рассмотрим проблемы второго типа. Целостность данных может быть нарушена, если в сети есть неисправности и данные периодически передаются неправильно. Для обеспечения доставки неповрежденных данных прикладным программам, использующим сеть, необходимо, чтобы сетевые протоколы позволяли передавать каждый блок данных несколько раз. В результате сетевые программы будут выполняться очень медленно. Единственный способ решить проблему целостности данных — изолировать, а затем заменить неисправную часть сети. Сообщества пользователей Мы рассмотрели различные факторы, влияющие на общую производительность системы. Однако мы не учли один из наиболее важных факторов — пользователей, которые запускают задания. При попытке проанализировать, каково влияние пользователей на производительность, может показаться, что именно из действий пользователей проистекают все проблемы: это такие создания, которые мешают системе работать нужным образом. Что может быть более далеким от истины, чем это утверждение? Компьютеры — это орудия труда, и они существуют для того, чтобы помогать пользователям выполнять работу, а не наоборот. Ограничения на используемую память и размер файлов, приоритеты заданий и т.д. будут эффективными только в том случае, если все пользователи сотрудничают. Нельзя заставить людей в обязательном порядке передавать свои задания для пакетной обработки (мм). Сотрудничество окажется успешным, если большинство пользователей понимают проблему и знают, что можно сделать для ее решения. Если решения навязаны сверху, а люди не понимают их целесообразности или даже считают, что они мешают работе, таким решениям будет оказано противодействие. Производительность работы всей системы в значительной степени зависит от категории ее пользователей: • Пользователи, выполняющие большое число относительно небольших заданий (например, пользователи, которые большую часть времени тратят на выполнение утилит UNIX и редактирование). • Пользователи, выполняющие небольшое число длительных заданий (например, пользователи, которые работают с программами моделирования, использующими файлы данных большого объема). • Пользователи, выполняющие небольшое число заданий, которые интенсивно используют центральный процессор и память, но не требуют столь же интенсивного применения устройств ввода-вывода. К этой категории относятся разработчики программ. Как правило, компиляторы являются большими программами, создающими внушительные массивы данных на диске. Обычно именно от них исходят проблемы, связанные с соперничеством за право обладания памятью. Конфликты могут возникать у представителей всех трех групп. Несколько десятков пользователей, которые выполняют команду grep и обращаются к удаленным файловым 628 Часть шестая. Управление процессами
39.12 системам, могут таким же образом влиять на общую производительность системы, как и несколько клиентов, работающих с гигабайтными файлами. Однако проблемы, вызываемые этими пользователями, не одинаковы. Например, установка "распределенной, файловой системы" приводит к повышению производительности работы с дисками продолжительных заданий, связанных с вводом-выводом. Однако это не помогает (а иногда даже мешает) пользователям, которые выполняют множество небольших заданий. Благодаря режиму пакетной обработки можно смягчить соперничество между длительными заданиями, переведя их в ночной режим, однако это не будет способствовать повышению производительности системы, если источником проблем являются пользователи, набирающие в редакторах текстовую информацию или просматривающие почту. Ситуацию усложняют также современные системы с сетевыми возможностями (i..u). Необходимо знать не только то, какого рода работу выполняют пользователи, но и какое оборудование они используют: стандартные терминалы, подключенные через линию RS-232, X-терминалы, подключенные посредством Ethernet, или бездисковые рабочие станции. Система X Window требует много памяти и увеличивает нагрузку на сеть. Нагрузку на сеть повышает также бездисковая рабочая станция. А разве меньше загружают сеть пользователи, обращающиеся к локальным или удаленным файлам через NFS или RFS? — ML, из книги System Performance Tuning издательства O'Reilly & Associates
40 Выполнение заданий по расписанию 40.01 Выполнение заданий в свободное время В наши дни, когда разделение времени и интерактивное программирование стали повсеместным явлением, многие пользователи UNIX забыли об одном из лучших способов применения системы с максимальной отдачей: о выполнении заданий в ночное время или в выходные дни. Большинство людей стремятся работать с 9 до 5, что составляет треть суток. (Хотя многие программисты предпочитают работать и в более позднее время!) Если для решения профессиональных задач использовать и остальное время (ночное время и выходные дни)-, производительность вашей системы может повыситься почти в четыре раза. Конечно, удобнее выполнять задания в интерактивном режиме, а не в ночное время, однако в последнем случае вам удастся избежать издержек на покупку дополнительных машин. Если использовать свободные часы, то при тех же аппаратных средствах можно выполнить намного больший объем работы. Есть несколько средств, предоставляющих возможность использовать свободное время для выполнения заданий: • Команда at (4о.ю) позволяет начинать выполнение заданий в произвольное время. Эта команда является стандартной для большинства систем UNIX. • Команда batch (40М), имеющаяся в System V.4 и SunOS 4.1, представляет собой простую (я бы даже сказал, упрошенную) систему пакетной обработки очередей заданий. В других UNIX-системах есть и более сложные пакетные системы. • Необходимо также знать о такой команде, как crontab (40.12), хотя она служит совершенно иным целям. Данная команда позволяет планировать периодическое выполнение заданий. • Наконец, не забывайте о команде sleep (40.02), если выполнение задания необходимо немного задержать или растянуть во времени. - ML 40.02 Если нужно немного подождать: команда sleep Команда sleep предназначена для того, чтобы ждать. Вот и все, что она должна делать. (К GNU-версии этой команды обычно добавляются различные излишества, но та команда sleep, которая имеется на компакт-диске, по своим возможностям совпадает со стандартной версией.) Так чем же она полезна? • С ее помощью можно создать простую утилиту напоминания, если у вас нет программы leave (4S.0S). Через десять минут (600 секунд) следующая команда выведет сообщение Time to go now... (Пора уходить...): 650 Часть шестая. Управление процессами
40.03 ()& 13.07 % (sleep 600; echo Time to go now....) 6 \S.o5 • Ее можно использовать в качестве аналога команды at (<ш.оз>, которая позволяет выполнить задание в указанное время (скажем, через три часа): % (sleep 10800; программа) 6 • С ее помощью можно наблюдать за программой (обычно — за сценарием интерпретатора shell), которая выполняется в фоновом режиме, и контролировать, какие процессы она запускает: % prog 6 [1] 12345 % sleep 5; ps PID TT STAT 18305 p4 S 18435 p4 S 18437 p4 D 18438 p4 R % 1 1 • 1 1 • 1 1 ; II- TIME 0:01 0:00 0:00 0:00 i i COMMAND -csh (csh) /bin/sh prog /bin/sort -r ps temp 18438 p4 R 0:00 ps !! 11.07 sleep 5; ps; sleep 5; ps; sleep 5; ps; sleep 5; ps; sleep 5; ps PID TT STAT TIME COMMAND ...прошло 5 секунд... PID TT STAT TIME COMMAND • Команда sleep удобна при выполнении последовательности команд, совместная работа которых может сильно перегрузить систему. В этом случае команда sleep организует паузы, в течение которых предыдущая команда успевает завершиться. Например, программа mail о.зз) запускает фоновые процессы для передачи почтовых сообщений. Посылая целый пакет писем, сделайте паузу на 5-10 секунд после отправки каждого письма: foreach 9.11 % foreach name ('cat people*) ? foxmltrprog $nama I mail $name ? sleep 10 ? end А вот другой пример. Уходя на обед, вы решили запустить на печать ряд документов. Позвольте и другим пользователям напечатать что-либо в промежутках между вашими заданиями: % lp bigfilel;sleep 600;lp bigfile2;sleep 600;lp bigfile3 - JP 40.03 Команда at Команда at позволяет указать, в какое время выполнить то или иное задание. Эта команда имеет следующий синтаксис: % at опции время < файл_сцвиаряя Указанный файл_сценария передается на выполнение в заданное время. Перенаправление ввода (<) не требуется в BSD и в некоторых других версиях UNIX. Если вы не хотите создавать файл сценария, то можете не задавать его, а прямо с терминала ввести команды, завершив ввод нажатием клавиш [CTRL-d]: % at опции время Команда._1 Комаида._2 ICTRL-dl Выполнение задений по расписанию 631
40.04 В большинстве случаев в качестве значения параметру время присваивается четырехзначное число, которое представляет время в терминах 24-часового цикла. Например, значение 0130 соответствует 1:30 ночи, а значение 14 00 — 2:00 дня. Можно также использовать такие сокращения, как lam, 130pm и т.п. — ML, из книги System Performance Tuning издательства O'Reilly & Associates 40.04 Выбор интерпретатора shell с помощью команды at В BSD UNIX посредством опции -с можно дать указание команде at (4о.оз) выполнить сценарий с помощью интерпретатора С shell, а с помощью опции -s — использовать для той же цели интерпретатор Bourne shell. По умолчанию команда at использует интерпретатор shell, с которым пользователь вошел в систему. Если вам повезет (а многим пользователям все-таки везет), то операция будет выполнена с первого раза. Но я знал многих пользователей, у которых возникали проблемы, когда они впервые вызывали команду at. Если вы — один из них, предлагаем кое-какие советы. Задания, запускаемые с помощью команды at, выполняются в среде, которая отличается от обычной рабочей среды. Будьте осторожны при использовании псевдонимов, функций и переменных интерпретатора shell, а также всего остального, что определено другими пользователями. Самый простой способ определить, что же случилось, состоит в том, чтобы заставить задание описывать состояние среды в каком-нибудь временном файле, а затем прочитать его, когда выполнение задания завершится: % at 1234 set 6.08 set > $HOME/at.set printenv 6.oi printenv > $HoME/at.env ICTRL-dl (В некоторых системах необходимо указать переменную $L0GDIR, а не $НОМЕ, и команду ейv, а не printenv.) Если вы используете интерпретатор типа csh или bash, который читает файл установок при запуске каждого экземпляра интерпретатора shell (а не только в начале сеанса работы с системой (2.08)), файл xshrc или .bashrc будет читаться всякий раз, когда задание запускается. Это имеет как положительную, так и отрицательную стороны. Положительная сторона состоит в том, что для задания, запускаемого командой at, можно устанавливать специфичные настройки интерпретатора shell. Однако если из файла xshrc запускаются интерактивные команды, задание может проигнорировать их или безнадежно зависнуть, ожидая ответа. Например, команда tfy (З.щ выведет сообщение об ошибке not a tty (не терминал). Попытка использовать команду tty для установки переменной интерпретатора shell может повлечь за собой не только выдачу сообщений об ошибках типа unset variable (невозможно присвоить значение переменной), но и то, что дальнейшее чтение файла .cshrc станет невозможным. И так далее... Это и есть отрицательная сторона. Убедиться в отсутствии интерактивных команд, которые выполнялись бы командой at, можно, либо выполнив проверку установки переменной prompt (2.09) в файле xshrc, либо проверив переменную $- (при использовании интерпретатора ksh или bash). Если же мне не нужны какие-то особые возможности интерпретатора shell, я ограничиваюсь тем, что просто ввожу команду at -s, чтобы выполнить задание в интерпретаторе Bourne shell. (Учтите, что некоторые системы используют интерпретатор ksh или bash, и именно он будет запущен, когда вы введете команду sh.) В нашей системе SunOS 4.1.3 существует другая проблема. В интерпретаторе С shell, который выполняет af-задания, система устанавливает переменную prompt. В результате интерпретатор читает мой файл xshrc, как будто я выполняю вход в систему! Ни одно из моих af-заданий не запускается, и я получаю сообщение о том, что переменная TERM не установлена. Как избежать подобной ситуации, рассказывается в параграфе 2.10. Мы предлагаем отличный способ обнаружения проблем, связанных с командой at, которые возникают в пользовательских файлах установок интерпретатора shell. Поместите на некоторое 632 Часть шестая. Управление процессами
40.06 время в первую строку своего файла .cshrc команду set verbose echo или добавьте команду set -xv (46.01) в первую строку файла ENV (для интерпретатора Кош shell) или .bashrc (для интерпретатора bash). Когда af-задание запустит интерпретатор shell, вы увидите многословные сообщения, информирующие о выполняемых командах и о переменных, которым присваиваются значения в файле инициализации. Возможно, вам будут предоставлены сведения о том, как интерпретатор выполняет команды самого af-задания. Вся эта информация направляется пользователю в качестве сообщения электронной почты, в котором поле Subject (тема) содержит строку output from your at job (Выводные данные вашего а/-задания). (По крайней мере, все происходит именно так при использовании тех версий команды at, с которыми мне приходилось сталкиваться.) А вот еще один совет относительно обнаружения проблем: поместите в критичные места своего файла установок команды, подобные следующим. echo "go to метка" | mail ваше_иия » 13.01 echo "go to метка" » $HOME/at.log Когда начнется выполнение ar-задания, эти команды позволят определить, на какой стадии возникла проблема. - JP, ML 40.05 Как избежать накладок при выполнении at- и сгоп-заданий Команды atq и at -I (40.09) являются более важными, чем может показаться на первый взгляд. Они демонстрируют график выполнения заданий и предоставляют вам возможность самостоятельно решать, когда выполнять свои задания. Я советую вам сначала запустить команду atq, и только после этого назначить время для запуска задания. Если этого не сделать, то впоследствии может оказаться, что ровно в полночь или в час ночи начнется выполнение дюжины больших заданий. В результате система окажется неработоспособной именно в тот момент, когда рядом никого не будет. Вот пример того, что может произойти, когда используются команды at операционной системы BSD: % atq Rank 1st 2nd 3rd 4th 5th Execution Sep 12, 1996 Sep 12, 1996 Sep 12, 1996 Sep 12, 1996 Sep 13, 1996 Date 01:00 01:00 01:00 01:00 02:00 Owner mikel johnt davek joek bobr Job# 4529 4531 •5532 4533 4534 Queue a a a a a Job Name trashsys.sh flame.sh stdin troffit stdin Получилось так, что четверо из пяти пользователей выбрали для запуска своих заданий первый час ночи. Следовательно, посреди ночи будут запущены четыре больших задания. Сможет ли система благополучно выполнить эти задания? А нельзя ли какое-то из них выполнить утром? Вместо того чтобы задавать выполнение заданий в час ночи, в полночь или в какое-либо другое время, определяемое целым числом, запустите их в различное время, указав при этом значения времени типа 3:48. Если ваш системный администратор заметит, что многие задания выполняются в одно и то же время, он может удалить некоторые задания и попросить вас запустить их в другое время. Если в вашей системе есть персональные сгопшЬ-файпы (40.12), вы не сможете увидеть сгои-задания других пользователей. Лучший способ уменьшить нагрузку на систему — выбрать для своих <тол-заданий "нестандартные" значения времени, например 4:37 утра. — ML, из книги System Performance Tuning издательства O'Reilly & Associates 40.06 Пакетная обработка заданий в System V.4 На многих UNIX-серверах системы пакетной обработки заданий считаются пережитком прошлого. Это ошибочное мнение. Благодаря системе пакетной обработки заданий вы сможете эффективно организовать выполнение большого объема работы, особенно на промышленных Выполнение заданий по расписанию 633
40.07 системах. Объединение заданий в пакет — один из лучших способов обеспечения загруженности компьютера в свободные часы. При использовании команды at выполнение заданий приобретает "пульсирующий" характер: наибольшая активность наблюдается в полночь, в час ночи, в два часа и в другое время, которое любят указывать в качестве начала выполнения. По мере завершения заданий нагрузка постепенно уменьшается. Если же сформирована очередь пакетных заданий, система равномерно работает на протяжении всего дня. В System V.4 и SunOS применяется разновидность команды at — команда batch, которая не позволяет указать, когда запустить задание на выполнение. Система имеет единую очередь заданий, которые выполняются в том порядке, в каком поступают в очередь. Синтаксис команды batch следующий: % batch Команд а_.2 Команда_2 lCTRL-d| Если ваше задание — это сценарий, его можно включить в очередь на пакетную обработку следующим образом: % batch имя_сценария Но все же сверьтесь с интерактивным руководством по команде batch, чтобы удостовериться, что в вашей системе разрешено применение этой команды. Посмотрите также, с каким интерпретатором shell можно использовать команду batch. Для удаления заданий из очереди предназначены команды atq и atrm (SunOS), а также at -I и at -r (V.4). По своей организации очередь удивительно проста: не поддерживаются обслуживание нескольких очередей, приоритеты и другие возможности, которые так необходимы во время пакетной обработки. Но при этом непременно соблюдается одно важное условие. Если пользователи помещают в очередь продолжительные задания, они твердо знают, что в любой момент времени будет выполняться максимум одна большая программа (будь то компилятор, программа моделирования или что-либо еще). Этого может оказаться достаточно для наведения порядка в системе. — ML, из книги System Performance Tuning издательства O'Reilly & Associates 40.07 Как избавиться от вывода at-заданий Большинство современных версий команды at шлют пользователю почтовые сообщения (Ш), содержащие все, что выводят команды af-задания. Некоторые пользователи пытаются "сбрасывать" эти данные в системную "мусорную корзину" /dev/null (Н.н) с помощью следующей командной строки: >& 13.05 % at время... >S /dev/null ...неправильно Эта команда не будет работать правильно, так как в файл /dev/null направляется вывод команды at. Эта команда всего лишь сохраняет свое задание в файле, чтобы системная программа выполнила его позднее. Т.е. команды, от вывода которых мы хотим избавиться, находятся в этом же файле. Если вы пользуетесь интерпретатором С shell, один из способов заставить команду at не выводить данные на экран, реализуется следующим образом: % at время... at> жахая-то команда >S /dev/null at> другая команда >S /dev/null at> ... и т.д. ... >& /dev/null at> ICTRL-dl В интерпретаторе Bourne shell это делается проще: $ at время. . . exec > 45.07 at> exec >& /dev/null 2>S1 634 Часть шестая. Управление процессами
40.09 at> кахая-то команда at> другая команда at> ... и т. д. ... at> |CTRL-d| Два замечания: • некоторые версии команды at имеют опцию -s, посредством которой задается выполнение задания с помощью интерпретатора Bourne shell; • не все версии команды at выдают приглашение at>, которое включено в пример. - JP 40.08 Автоматический повторный запуск at-заданий Иногда удобно создать задания, которые выполняют свою работу, завершаются и автоматически задают время своего следующего запуска. Ниже показано, как это сделать: #!/bin/sh myself=/home/mikel/bin/restarter # Далее следуют любые команды sleep 60 at -s 0123 tomorrow Smyself Если запустить этот сценарий, он будет выполняться ежедневно в 1:23 ночи. Команда sleep (40.02) обеспечивает выполнение следующей команды at после 1:23 ночи. Это является непременным условием того, что очередное задание будет выполнено именно завтра, а не сегодня. В большинстве систем нет необходимости в этом приеме, но сам по себе он неплох. Следует отметить, что задания, которые сами себя запускают, являются пережитком прошлого, когда рядовые пользователи предпочитали избегать применения такой команды, как cron (4o.i2). В связи с тем, что в настоящее время пользователи могут иметь персональные avntab-файпы, необходимость в самозапускающихся заданиях уменьшилась. Если окажется, что вам все же необходимо написать сценарий, запускающий себя повторно, пожалуйста, позаботьтесь о том, чтобы после него в системе не оставалось никакого "мусора". Если программа больше вам не нужна, не забудьте удалить ее с помощью команд atq и atrm (4ощ. - ML 40.09 Проверка и удаление заданий Время от времени вы выполняете af-задание и видите, что с ним происходит что-то неладное. Как изъять такое задание из очереди? Для этого вам потребуются две команды — atq, предоставляющая сведения о заданиях, которые находятся в очереди, и atrm, удаляющая задания из очереди; Команда atq достаточно простая. По умолчанию она выводит перечень всех заданий, которые включены в очередь. В качестве необязательного аргумента команды atq можно задать имя пользователя. В этом случае команда сообщает обо всех заданиях, которые включены в очередь указанным пользователем. Список заданий выглядит приблизительно так: los% atq Rank Execution Date Owner Job# Queue Job Name 1st Oct 9, 1996 22:27 mikel 4637 a stdin 2nd Oct 10, 1996 01:08 mikel 4641 a stdin 3rd Oct 10, 1996 02:34 judy 4663 a stdin Обратите внимание: команде atq не запрещено сообщать вам о заданиях других пользователей. Такая информация очень полезна (см. парафаф 40.05), хотя возможность ее получения можно расценить как брешь в системе безопасности. Задания в списке упорядочиваются по времени запуска. Если задана опция -с, критерием сортировки заданий является время помещения в очередь. (Команда atq -я просто выводит число заданий, которые помещены в очередь. Не представляю, для чего это может понадобиться.) Выполнение заданий по расписанию 635
Задание, номер которого вам известен, можно удалить из очереди с помощью команды atrm. Вы сможете удалить только свои задания, но не чужие: % atrm 4637 4 637: removed % atrm 4663 4663: permission denied Команда atrm удаляет из очереди все задания, которые вы туда поместили. Она полезна, когда вы хотите удалить всю свою очередь. Примечание: В System V до версии SVR4 для вывода списка заданий, находящихся в очереди, предназначена команда at -l (аналог команды atq), а для удаления заданий конкретного пользователя — команда at -г (аналог команды atrm). Некоторые BSD-системы более ранних версий не поддерживают ни одной из описанных возможностей. Если вы поместили задание в очередь, то сможете удалить его, очистив файл с заданием (24.оо, который находится в каталоге /usr/spool/at. Можно также попросить суперпользователя (i.24) добраться до этого каталога и вручную удалить файл с заданием. - ML Сценарии nextday и nextweekday: завтра и на следующий будний день Еще до того как. в UNIX-системах, с которыми я работал, появились персональные crontab- файлы (40.12), мне пришлось поломать голову над тем, как добиться повторения gf-зада- ния (40.03) на следующий будний день (пропуская субботу и воскресенье) или каждый день. В нашей системе команда at была незамысловатой и не воспринимала дату, заданную в виде now + 1 day. С этой задачей справлялся сценарий, имевший два имени — nextday и nextweekday. Я вызывал его из своего af-задания следующим образом: % cat atjob команда sleep 60 '...'9.16 at 2325 'nextweekday" < atjob В четверг команда в последней строке этой группы будет иметь вид at 2325 Friday < atjob (Friday — пятница). В пятницу она примет вид at 2325 Monday < atjob (Monday — понедельник). Если же я воспользуюсь именем nextday, то получу команду at 2325 Saturday < atjob (Saturday — суббота). При указании опции -и возвращается номер буднего дня. Примечание: Этот сценарий работает только с некоторыми версиями команды date. Если ваша версия команды не воспринимает формат строк +%формат, инсталлируйте команду date (Si.W)c компакт-диска. Этот сценарий можно инсталлировать с компакт-диска или из сетевых источников (52.07). Во втором случае укажите команде tar инсталлировать сценарий nextday и все ссылки на него: % tar xvf архив.tar nextday nextweekday x nextday, 1564 bytes, 4 tape blocks nextweekday linked to nextday Чтобы определить, какую команду выполнять, сценарий анализирует значение переменной $0 (т.е. имя, под которым он вызывается). - JP 636 Часть шестая. Управление процессами 40.10 40.10 (9) nextday
40.12 40.11 Посылайте себе письма с напоминаниями Я использую команду at (40.07), чтобы посылать самому себе напоминания. Задание, которое запускает команда at, выполняет программу mail (1.33) и подает текст сообщения на стандартный ввод программы, отправляющей почту. Приведем ряд примеров: 1. Чтобы отправить напоминание, состоящее из одной строки, я использую однострочную команду следующего вида: % at 0427 tuesday at> echo "Сегодня нужно отправить саодку Тиму" | mail jpeek@jpeek.com at> |CTRL-d| В данном случае почтовое сообщение будет послано во вторник в 4:27 утра. Сообщение гласит: "Сегодня нужно отправить сводку Тиму". 2. Для отправления сообщения, состоящего из нескольких строк, можно воспользоваться временным файлом: % vi msgfile ...текст сообщения поместите в файл msgfile... % at OBOB feb 28 at> mail jpeek@jpeek.com < msgfile at> rm msgfile at> ICTRL-dl 3. Объедините вывод команд UNIX и текст с помощью обратных кавычек (9.16) и конструкции "документ здесь" (s.isy. % at 0115 ас> mail -s "Тем, кто прилежно работает" torn « END at> Эти служащие работают допоздна. Они заслужили премию: at> -w" at> END at> ICTRL-dl % Посредством этой команды сообщение направляется пользователю по имени torn в 1:15 ночи. (В данном случае программе mail указывается тема сообщения с помощью опции s в командной строке. Вывод команды w содержит подробную информацию о пользователях, работающих на машине. Этой возможностью обладают не все системы.) Если вы не знаете, как вставить специальные символы в текст конструкции "документ здесь" (45.26), не используйте других символов, кроме букв, цифр, запятых и точек. Если ваш системный администратор установил программу calendar (4S.04), в вашем распоряжении окажется очень удобное средство отправления однострочных напоминаний в конкретные дни. При условии, что ваша система поддерживает персональные с/юя/дб-файлы (40.12), напоминания можно посылать периодически (например, каждый вторник, каждый час и т.д.). Для этого используйте команды, приведенные в примерах 1 и 2. - JP 40.12 Периодическое выполнение заданий: программа стоп Программа стой позволяет пользователям спланировать периодическое выполнение программ. В частности, данную программу можно использовать для того, чтобы каждый час опрашивать конкретный UUCP-узел (1.зз), каждую ночь удалять резервные файлы, созданные редактором, а также для решения многих других задач. Однако программа стоп не подходит для планирования одноразового выполнения программ. Для этих целей предназначена команда at (40.03). Программа стой может посылать вывод выполняемого задания либо в регистрационный файл путем перенаправления (i3.oi), либо пользователю посредством электронной почты о.зз). Выполнение заданий по расписанию 637
40.12 Примечание: Слэл-задания выполняются системной программой в среде, которая существенно отличается от той, где проходят обычные сеансы работы пользователя с системой. В процессе работы с командой сгоп, как правило, используется краткая последовательность поис- ка (8.07), поэтому необходимо указывать полные путевые имена программ, которые находятся вне стандартных системных каталогов. Будьте осторожны, используя псевдонимы команд, функции и переменные интерпретатора shell, а также остальные установки, предназначенные для других пользователей. Планирование выполнения заданий Программа сгоп обслуживается демоном (i.U) стп. Что и когда выполнять, указывается в записях таблицы crontab. В BSD эта таблица хранится в файлах /usr/lib/crontab и /usr/lib/cron- tab. local. Для сохранения элементов таблицы может быть использован каждый из этих файлов. Оба файла являются ASCII-файлами и могут быть модифицированы с помощью любого текстового редактора. Так как обычно только пользователь root имеет доступ к этим файлам, все вопросы по планированию выполнения заданий должны быть согласованы с ним. Это может быть как преимуществом, так и недостатком, что зависит от потребностей и персонала вашего вычислительного центра. В System V (и во многих других версиях UNIX) любой пользователь может дополнить таблицу crontab. Элементы таблицы для каждого пользователя сохраняются в отдельном файле. Эти файлы не редактируются непосредственно пользователями. Информация заносится в них с помощью команды crontab, которая описана в следующем параграфе. [По своему опыту знаю, что сгоя-задания запускаются из начального каталога пользователя. Если вы считываете задания из файла или перенаправляете вывод в файл с относительным путевым именем (Ы.Ю), то последний, скорее всего, находится в вашем начальном каталоге. Выясните этот вопрос в своей системе. — JP\ Элементы crontab-фаша предписывают демону сгоп выполнять задания через равные промежутки времени. Каждый элемент состоит из одной строки и имеет следующий формат: минуты часы день_месяца месяц день_недели имя_пользователя команда (BSD) минуты часы день_месяца месяц день_недели команда (System V) Поля разделяются пробелами. Пробелы могут содержаться только в конечном поле — команда (это поле включает все, что следует за полем день_недели). Другие поля не должны содержать пробелов. Поле имя_пользователя применяется только в BSD и включает имя пользователя, который задает выполнение команды. В System V команды выполняются с правами того пользователя, который является владельцем crontab-фатлла, откуда извлекаются эти команды. Имя данного файла совпадает с именем пользователя. В пяти первых полях указывается время выполнения команды. Соответствующие значения описаны в табл. 40.1. Таблица 40.1. Поля записи в crontab-файле, задающие время Попе минуты часы день месяца месяц день_недели Значение Минуты, отсчитываемые по указанного часа Время суток День в месяце Месяц в году День недели истечении Диапезон 0-59 0-23 (0 — полночь) 1-31 1-12 1-7 (1 — понедельник) BSD 0-6 (0 — воскресенье) System V Содержимым любого из этих полей может быть отдельное число, пара чисел, разделенных знаком тире (символ диапазона), список чисел и диапазонов, разделенных запятыми, или звездочка (метасимвол, соответствующий любому значению, действительному для данного поля). Если первый символ в поле — знак диеза (#), команда сгоп будет рассматривать содержимое поля как комментарий и проигнорирует его. Это простой способ блокировать на время содержимое поля, не удаляя его полностью. 638 Часть шестая. Управление процессами
40.12 Приведем пример записей cmntab-файла (в формате System V): 0,15,30,45 * * * * (echo -n ' '; date; echo "") >/dev/console 0,10,20,30,40,50 7-18 * * * /usr/lib/atrun 7 0 * * * find / -name "*.bak" -type f -atime +7 -exec rm {} \; •&! 45.21 12 4 * * * /bin/sh /usr/adm/ckdsk >/usr/adm/disk.log 2>sl 22 2 * * * /bin/sh /usr/adm/ckpwd 2>S1 | mail root 30 3 * * 1 /bin/csh -f /usr/lib/uucp/uu.weekly >/dev/null 2>sl \% 40.14 12 5 15-21 * * test 'date +\%a' = Mon ss /usr/local/etc/mtg-notice #30 2 * * 0, 6 /usr/lib/newsbin/news.weekend Первая запись задает вывод даты на консольный терминал каждые пятнадцать минут. Обратите внимание на то, что несколько команд заключены в скобки. Это сделано для того, чтобы перенаправить вывод всей группы команд. (С технической точки зрения, это служит указанием выполнять команды вместе в порожденном интерпретаторе shell оз.от)) Вторая запись задает выполнение программы /usr/lib/atrun каждые десять минут с 7:00 утра до 6:00 пополудни ежедневно. Третья запись указывает выполнить команду find через семь минут после полуночи, чтобы удалить все резервные файлы (баЛ-файлы), к которым не было обращения на протяжении семи дней. [Чтобы удалить другие ненужные файлы, попытайтесь объединить несколько команд find (23.22). Кроме того, как указывалось в параграфе 40.05, не пытайтесь приурочить выполнение своих заданий к "круглым" значениям времени (1:00, 2:00 и т.д.); выбирайте необычные значения, такие как 4:12 ночи. — JP] В четвертой и пятой записях задается выполнение сценария в 4:12 и 2:22 ежедневно. В обоих случаях в командных строках явно указывается интерпретатор, в котором должны выполняться сценарии. Если интерпретатор shell не задан явно, по умолчанию обычно используется Bourne shell. В обеих записях выполняется перенаправление стандартных потоков вывода и ошибок. В одном случае они направляются в файл, а в другом — пользователю root (с помощью команды mail). В шестой записи задано выполнение сценария uu.weekly, который находится в каталоге /usr/lib/uucp, в 3:30 ночи каждый понедельник. Обратите внимание на то, что в этой записи используется синтаксис команд интерпретатора Bourne shell, в частности при перенаправлении вывода, хотя сам сценарий будет выполняться под управлением интерпретатора С shell. В седьмой записи указано выполнять команду в третий понедельник каждого месяца. Подробнее об этой команде сказано ниже. Последний элемент задавал выполнение команды /usr/lib/newsbin/news.weekend в субботу и воскресенье в 2:30 ночи, но был отменен с помощью знака #. (Знак # можно также использовать для вставки комментария в cmntab-файл.) Четвертый и пятый элементы иллюстрируют три альтернативных способа вывода: перенаправление в файл, направление по каналу программе mail и сброс в файл /dev/null. Если перенаправление вывода не выполняется, с помощью программы mail выходная информация посылается пользователю, который активизировал команду. Поле команда может содержать любую команду UNIX или группу команд, разделенных точкой с запятой. Длина сгои/об-элемента может быть произвольной, но в файле он должен быть представлен в виде единой строки. Единственная проблема, связанная с синтаксисом cmntab-эпшета, заключается в том, что хотя он позволяет указывать любой день месяца и любой день недели, применение синтаксических конструкций типа "третий понедельник каждого месяца" недопустимо. Может показаться, что такой конструкции соответствует следующий элемент: 12 5 15-21 * 1 команда Но это не так. Данный элемент задает выполнение команды каждый понедельник, а также с 15-го по 21-е число каждого месяца.* Решение этой проблемы, предложенное Грегом Уббеном, содержится в седьмом слэийб-элементе. Грег Уббен использует команды test (44.20) и date (5i.W), чтобы сравнить название сегодняшнего дня (например, Tue — вторник) с Эта специфичная особенность System V каким-то образом повлияла и на остальные системы. "Настоящие" BSD-системы действуют так, как объяснялось раньше. Однако в системах SunOS 4.Л" за образец взят подход, принятый в системах System V. А с появлением Solaris в мире осталось сравнительно немного настоящих коммерческих BSD-систем. Выполнение заданий по расписанию 639
40.13 названием того дня, в который необходимо выполнить стол-задание (в данном случае — Моп, понедельник). Это задание будет выполняться каждый месяц в период с 15-го по 21-е число. В этот период команда mtg-notice будет выполняться только в понедельник. Оператор && (44.Ю) интерпретатора shell разрешает выполнение команды mtg-notice только в том случае, если проверка, проведенная командой test, завершилась успешно. Грег записал элемент так, как показано ниже, выполняя проверку даты с помощью команды test 12 5 15-21 * * test "date + \%а' != Mori || /usr/local/etc/mtg-notice Он провел проверку на отрицательный результат для того, чтобы код завершения сгои-задания был нулевым (успешное завершение) в том случае, когда не выполняется команда mtg-notice. Этот прием пригодится и вам. Демон сгоп активизируется при загрузке системы в одном из сценариев инициализации. Каждую минуту он считывает ш>и/а/>-файл(ы) с целью обнаружения изменений. Следовательно, любое изменение расписания для с/ииГаб-элементов будет приведено в действие в течение одной минуты. - AF, JP 40.13 Добавление новых crontab-элементов Большинство последних версий UNIX имеют специальные команды для управления с/ии/а^файлами. Чтобы создать новый crontab-файл, сначала следует образовать файл, содержащий все нужные элементы. Затем надлежит выполнить команду crontab, чтобы инсталлировать файл в каталоге системы сгоп. Например, если пользователь chavez выполнит приведенную ниже команду, файл тусгоп будет создан в каталоге /usr/spool/cron/crontabs/chavez. 5 crontab mycron Если к этому моменту у пользователя chavez уже создан один crontab-файл, все элементы этого файла будут замещены теми элементами, которые содержатся в файле тусгоп. Если пользователь chavez хочет сохранить какие-то текущие crontab-эпемъты, он должен включить их в файл тусгоп. При указании опции -/ команда crontab выводит список всех сголгаб-элементов. Этот вывод можно направить в файл, а затем загрузить в редактор: $ crontab -1 >mycron 5 vi mycron $ crontab mycron При указании опции -г все текущие сгоиСа/кэлементы удаляются. В системе SunOS команда crontab имеет дополнительную опцию —е, позволяющую отредактировать текущие c/wj/йй-элементы в один прием. (В параграфе 40.15 приведен сценарий, демонстрирующий решение этой задачи.) В BSD-системах нет команды crontab, а у пользователей нет персональных crontab-файлов. В таких системах проводится различие между "глобальными" (файл /usr/lib/crontab) и "локальными" (файл /usr/lib/crontab.local) сгоиГай-элементами. Вы должны непосредственно редактировать эти файлы, для чего, возможно, вам потребуется стать суперпользователем. Это хорошая идея — собрать личные и специфические для данного узла crontab-эпементы в файле /usr/lib/crontab.local. — AF, из книги Essential System Administration издательства O'Reilly & Associates 40.14 Включение стандартного ввода в crontab-элементы Поскольку crontab-запнсь должна располагаться в одной строке, в этой записи трудно задать обработку данных из стандартного входного потока. Безусловно, вы можете вводить команды следующего вида: О 22 * * * echo "It's 10PM; do you know where your children are?" | wall Однако при этом нельзя использовать конструкцию "документ здесь" и другие методы формирования многострочного ввода, поскольку они требуют наличия нескольких строк. 640 Часть шестая. Управление процессами
40.15 Программа сгоп позволяет решить эту проблему. В ней предусмотрена возможность включать стандартный ввод прямо в командную строку. Если команда содержит знак %, программа сгоп использует любой текст, следующий после этого знака, как стандартный ввод для этой команды. Путем добавления новых знаков процента текст можно разбить на строки. Например, если задать следующий с/ии/йй-элемент: 30 11 31 12 * /etc/wall%Happy New Year!%Let's make next year great! то 31 декабря в 11:30 вечера будет запущена команда wall, которая выведет сообщение для всех пользователей системы: Happy New Year! Let's make next year great! [Если знак процента должен непосредственно присутствовать в столГаб-элементе, например в команде типа date +%a, поставьте перед ним обратную косую черту. — JP] — AF, из книги Essential System Administration издательства O'Reilly & Associates 40.15 Сценарий crontab упрощает редактирование crontab-элементов и делает его более Безопасным Иногда я допускал ошибки при вводе команды crontab и случайно удалял свой crontaЬ-файл, не имея юзможности восстановить его. Однако мне нравится исцользовать команду crontab -e системы SunOS для интерактивного редактирования. Поэтому я создал специальный сценарий. Чтобы не вызвать случайно системную версию команды crontab, я сохранил этот сценарий в каталоге, указанном в самом начале переменной PATH (S.o7). Когда же мне необходима системная версия команды crontab, я ввожу ее полное путевое имя. #! /bin/sh cmd=/usr/bin/crontab # СИСТЕМНАЯ ВЕРСИЯ # ПОКАЗАТЬ ПОЛЬЗОВАТЕЛЮ, ЧТО ОН ВЫПОЛНЯЕТ: echo "Running Jerry's crontab command..." 1>&2 case 5# in 1) ;; # ВСЕ В ПОРЯДКЕ *) echo "Usage: Vbin/basename $CT —e | —1 | -d"; exit 1 ;; esac case "$1" in -[id]) ?cmd $1 ;; # ВЫЙТИ, ВОЗВРАЩАЯ КОД СОСТОЯНИЯ СИСТЕМНОЙ КОМАНДЫ -е) # ОТРЕДАКТИРОВАТЬ: umask 077 stat=l # КОД ВОЗВРАТА ПО УМОЛЧАНИЮ; ПЕРЕУСТАНОВИТЬ В 0 ДЛЯ НОРМАЛЬНОГО ЗАВЕРШЕНИЯ start=/tmp/CRONTAB$$s end=/tmp/CRONTAB$$e trap 'rm -f $start $end; exit $stat' 0 1 2 15 $cmd -1 > $start I I exit # ПОЛУЧИТЬ КОПИЮ ФАЙЛА CRONTAB /bin/cp $start $end $(VISUAL-?(EDITOR-vi}} ?end if cmp -s $start $end then echo "The crontab file was not changed." 1>&2; exit else $cmd $end Stat=$? # ВЫЙТИ, ВОЗВРАЩАЯ КОД СОСТОЯНИЯ СИСТЕМНОЙ КОМАНДЫ crontab exit fi *) echo "Usage: Vbin/basename $0* -e | -1 I -d"; exit 1;; esac JP umask 22.04 trap 44.12 || 44.09 ${..-..) 45.12 cmp 2S.11 Выполнение заданий по расписанию 21 9-171 641
Часть седьмая Терминалы и принтеры В этой книге не слишком много внимания уделяется аппаратным средствам UNIX, однако для темы "Терминалы и принтеры" следует сделать исключение. Ведь без этих устройств ввода-вывода все остальное теряет свое значение. Мы уже сказали несколько слов о настройке терминала в главе 5. Главы 41 и 42 служат продолжением этой темы. В главе 41 даны базовые сведения об интерфейсе терминала UNIX. В главе 42 внимание акцентировано на проблемах, которые могут возникнуть при работе с терминалом, а также на способах их устранения. Глава 43 посвящена вопросам печати в UNIX. В ней рассматриваются не только основы организации буфера печати, но и способы получения форматированного вывода. Кроме того, эта глава содержит описания некоторых полезных утилит преобразования изображений и создания PostScript-файлов. - TOR 21*
41 Настройка терминала и последовательного порта 41.01 0 чем пойдет речь в главе Мы уже затрагивали тему управления терминалами в UNIX в ряде параграфов главы 5. Всесторонне данная тема освещена в настоящей главе. Новички не всегда знают, что для работы с терминалом и другими последовательными устройствами существует несколько механизмов. Такие программы, как tset и tput, а также базы данных termcap и terminfo, которые используются этими программами, конфигурируют терминал или окно (или же сообщают их характеристики другим программам). В то же время команда stty модифицирует работу драйверов тех устройств UNIX, которые управляют последовательным портом. Такое разграничение не всегда понятно пользователю, особенно потому, что программа tset выполняет некоторые свои функции на обоих уровнях. Возможно, мы лишь привнесем путаницу, рассматривая обе темы в одних и тех же главах. Ничего не поделаешь! В данной главе мы продолжим изучать работу терминалов в UNIX, но уже более углубленно. • В параграфе 41.02 объясняется, почему команда stty является такой сложной. В параграфе 41.03 показано, как проверить установки этой команды. • Параграф 41.04 содержит сведения о том, как терминал обрабатывает символы табуляции. • В параграфе 41.05 рассказывается, почему System V и BSD UNIX по-разному обрабатывают то, что пользователь вводит в командной строке. • В параграфах 41.06 и 41.07 даны советы относительно применения команды stty. • В параграфе 41.08 объясняется, как программы, предназначенные для работы с терминалами, могут выполняться под управлением оконных систем и какова в этом случае роль "псевдотерминалов" (pty). • В параграфах 41.09 и 41.10 рассказывается, как пользоваться управляющими последовательностями, чтобы изменять настройки терминала. • В параграфе 41.11 приведены инструкции относительно того, как прочитать содержимое баз данных termcap и terminfo. • В параграфе 41.12 описано, как избежать чтения содержимого баз данных termcap и terminfo. • Параграф 42.01 знакомит с некоторыми другими концепциями управления терминалами на уровне драйвера. - TOR Настройка терминапа и последовательного порта 645
41.02 41.02 Команда stty и материал, с которым она работает [...полезный материал! Ознакомившись с настоящим параграфом, вы получите базовые сведения, которые необходимы для понимания взаимодействия между компьютером и терминалом или окном пользователя. Крис занимается этим очень давно. Интересна также история этого вопроса. Во время чтения данного параграфа полезно иметь под рукой интерактивное руководство по команде stty. — JPJ Допрос: Каковы функции команды stty? Почему она имеет столько опций? Ответ: Применение последовательных портов в частности и компьютерные коммуникации в целом представляют собой достаточно сложную сферу деятельности. Требования, предъявляемые к связи через последовательный порт, а также средства поддержки последовательных портов в UNIX вначале были очень простыми, но затем претерпели стремительную и практически неконтролируемую эволюцию. Управление терминалами: как это делалось раньше Изначально ОС UNIX работала на малых машинах, связь с которыми поддерживалась только через телетайпы (Ну). В задачи ядра UNIX входило принимать командные строки, в которых позволялось выполнять минимум исправлений: стирать предыдущий символ и удалять введенную строку (полностью). Кроме того, ядро преобразовывало несколько "специальных" символов, предназначенных для управляющих программ. Телетайпы представляли собой печатающие устройства и не могли выполнять фактическую операцию удаления. Поэтому символами стирания и удаления служили обыкновенные печатаемые символы — # и @. Первоначальными специальными символами были [CTRL-d] (обозначение конца файла), DEL (прерывание) и [CTRL-\] (выход из программы). Кроме того, ядро связывало коды клавиши [RETURN] с символом новой строки, чтобы клавишу [RETURN] можно было применять на телетайпах, на которых она присутствовала. Эти телетайпы имели некоторые особенности. В частности, в них использовалась движущаяся печатающая головка (или каретка), которой требовалось значительное время, чтобы переместиться от правого края листа к левому. Если на печать посылался непрерывный текст, телетайп мог во время возврата каретки размазывать символы по всей странице.* Поэтому ядру UNIX приходилось делать паузу после возврата каретки. В то же время ядро выполняло "обработку вывода", заменяя при необходимости символы новой строки кодами возврата каретки и перевода строки, которые использовались телетайпом." Ряд телетайпов поддерживал только прописные буквы, поэтому в UNIX пришлось создать дополнительные средства для поддержки этих устройств. В некоторых отношениях UNIX удалось избежать повторения других операционных систем. Однако в этой системе предполагалось, что все телетайпы являются "дуплексными" и используют "удаленное эхо". Это означало, что как телетайп, так и UNIX могут посылать друг другу данные в одно и то же время, и при этом телетайп не будет печатать символы, пока не получит соответствующее указание от компьютера, управляемого UNIX.*** Операционная система должна была позволять таким специальным прикладным программам, как UUCP (из), получать символы без какой-либо обработки (так называемый режим необработанных данных). Ядро предоставляло подобную возможность по принципу "все или ничего". В режиме необработанных данных каждый вводимый и выводимый символ оставался таким, каким он был введен, и немедленно передавался непосредственно прикладной программе. В режиме обработки данных ядро выполняло преобразование вводимых и выводимых символов, а также делало необходимые задержки. * Это преувеличение. Печать во время возврата каретки изредка практиковалась в диагностических целях для проверки скорости мотора. Предполагалось, что символы, которые печатались во время возврата каретки, будут появляться в междустрочных промежутках. ** Некоторые телетайпы действительно обрабатывали символ новой строки как "новую строку", т.е. выполняли возврат каретки н перевод строки. Но в большинстве случаев эту функцию должен был нести компьютер. •"Понятие "дуплекс" вышло из употребления, поскольку теперь все устройства являются дуплексным или, по крайней мере, имитируют этот режим. 646 Часть седьмая. Терминалы я принтеры
41.02 Помимо режима ядро позволяло менять каждый из специальных символов (5.09). Кроме того, предоставлялась возможность управлять некоторыми простыми параметрами интерфейса последовательного порта (такими как контроль четности* и скорость передачи в бодах). Скорость передачи первого телетайпа равнялась ПО битам, или 11 символам, в секунду (бит/с). (В действительности машины выполняли ровно 11 операций печати в секунду и бездействовали только тогда, когда нечего было печатать.) Первые модемы для компьютеров имели скорость 110 и 300 бод. Существовал стандартный набор скоростей для последовательного порта: 50, 75, ПО, 134.5, 150, 200, 300, 600, 1200, 1800, 2400, 4800 и даже 9600 бит/с, что считалось очень высокой скоростью. UNIX-системы использовали платы для последовательного порта с двумя дополнительными скоростями, обозначенными как А и В, или как exta и extb. Некоторые UNIX-системы до сих пор поддерживают именно эти (и только эти) скорости, где exta равна 19200 бит/с, a extb — 38400 бит/с. Со временем другие устройства стали вытеснять телетайпы. Сначала появились так называемые "стеклянные" телетайпы — экранные устройства, при разработке которых была предпринята попытка повторить все функции телетайпа. Затем увидели свет более "умные" терминалы, на экранах которых можно было перемешать курсор произвольным образом и редактировать выведенные данные (что по тем временам было настоящим чудом). Для выполнения операций редактирования эти терминалы использовали специальные управляющие последовательности. С появлением таких терминалов стало возможным написание полноэкранных редакторов. UNIX пришлось эволюционировать, чтобы приспособиться к новым тенденциям. К сожалению, в то время существовало две главные ветви UNIX. Одна со временем превратилась в систему BSD 4, или Berkeley UNIX, а другая послужила основой для System V. Цели обеих систем были сходными, поэтому в них были заложены похожие подходы к управлению терминалом. Однако разработчики Berkeley UNIX наряду с попыткой сохранить совместимость системы приложили усилия, чтобы создать хороший пользовательский интерфейс. В то же время в первоначальной версии System V разработчики отказались от совместимости в пользу эффективности и "полноценного" интерфейса, который позволял бы использовать все возможности последовательного порта. Итак, Berkeley UNIX была наделена тремя режимами терминала. Она поддерживала традиционные режимы необработанных данных (raw mode) и обработки данных (cooked mode), а также новый режим неполной обработки данных (half-baked mode), называемый также cbreak. Хотя в режиме cbreak и выполнялись какие-то операции по обработке входных данных, большинство символов передавались прикладной программе в том виде, в каком они были получены с терминала. Поскольку ядро не накапливало строки, символы стирания и удаления строки не требовались: строки посылались программе без каких-либо изменений. Большинство команд управления процессами — "прервать", "выйти", новые коды "остановить" и "приостановить" — интерпретировались в системе прежним образом. Чтобы позволить пользователям набирать эти команды, был введен новый символ "далее следует литерал". Система Berkeley UNIX отличалась еще и тем, что в ней была усовершенствована обработка вывода: внедрена возможность специального преобразования для некоторых дисплеев корпорации Hazeltine, появились такие свойства, как правильное управление символами табуляции, блочный вывод, стирание слов. С другой стороны, в базовой версии System V вообще отсутствовал режим необработанных данных. В отличие от Berkeley, в System V позволялось управлять каждой характеристикой режима ввода/вывода в отдельности. Так, с помощью опции icanon указывалось, следует ли копить входные строки. Посредством опции isig осуществлялось управление сигналами: когда эта опция была выключена, символы "прервать" ([DEL], а в современных системах — [CTRL-c]) и "выйти" становились обычными символами. Опция inpchk употреблялась с целью * Данный режим используется для контроля над ошибками. Если имеется значение 1001001 и выполняется контроль четности, бит четности должен быть единицей, так как значение 1001001 содержит три единицы — нечетное число — и добавление еще одной единицы делает его четным. Если бит четности не согласуется с полученным числом, то по крайней мере один бит этого числа неправильный. Конечно, таким битом может быть и сам бит четности. Более того, в случае последовательного порта может не быть средств, позволяющих исправить ошибку (в большинстве UNIX-систем ядро просто удаляет неправильный символ), хотя контроль все-таки возможен. Настройка терминала и последовательного порта 647
41.02 установки для входных данных режима контроля четности и т.д. Для обработки вывода тоже применялись индивидуальные опции: ocrln — для управления преобразованием "возврат каретки — перевод строки", a opost — для обработки выходных данных в целом. Выключив все опции, в прикладной программе можно было достичь того же результата, что и в режиме необработанных данных, который применялся в старых системах. Ядро позволяло также управлять количеством битов в каждом последовательном пакете данных, количеством стоп-битов и т.д.* Таким образом, хотя в Berkeley UNIX имелась весьма полезная возможность редактирования строк, зато в ней было невозможно подключать системы, использующие пятибитовую кодировку Бодо. В System V отсутствовали многие возможности интерфейса пользователя, зато она могла взаимодействовать почти с любым устройством. С тех пор мир стал, с одной стороны, проще (старые телетайпы, например, практически полностью исчезли), а с другой — гораздо сложнее. В наши дни на многих компьютерах чаще используются растровые дисплеи, а не индивидуальные удаленные терминалы. UNIX-системы поддерживают работу в сети и применяют оконные системы, подобные X Window (Ui). Это влечет за собой появление несметного числа опций, систем управления окнами, различных диалоговых систем и т.д. Все они имеют одно общее свойство: чтобы выполнять с их помощью старые прикладные программы, для каждого оконного или сетевого сеанса работы с системой необходим интерфейс виртуального терминала. UNIX-системы предоставляют этот интерфейс посредством псевдотелетайпов (псевдотерминалов), или pty (4I.08). Каждый псевдотерминал предназначен для эмуляции дисплейного терминала, который, в свою очередь, рассматривается системой как телетайпное печатающее устройство. (Иногда удивляешься, в чем же здесь прогресс!) Комитет по стандартизации POSIX установил стандартный интерфейс как для уровня ядра UNIX, так и для команды stty. Большинство UNIX-систем, включая Berkeley UNIX, адаптированы в соответствии с требованиями этого стандарта. Стандарт предоставляет разработчикам некоторую свободу действий (в основном из соображений совместимости с System V): он позволяет системам использовать как гибкость интерфейса System V, так и особенности подхода в Berkeley UNIX. Это означает, что хотя эмуляция старых терминалов по-прежнему будет выполняться, она, по крайней мере, будет производиться одинаковым образом. Основные принципы обработки символов Посмотрим, что происходит с введенным символом с момента нажатия клавиши до момента, когда прикладная программа отреагирует на его ввод. Как протекает этот процесс, зависит от используемой системы, а также от того, работаете вы с окном, терминалом или сетью. Однако в каждом случае его сущность остается неизменной. Мы будем исходить из того, что пользователь применяет в работе обычный терминал (далее — терминал). Его представление в ядре назовем "tty". Допустим, вы ввели букву х. Терминал посылает ASCII-код (si.oj) для строчного символа X (120) представлению терминала в ядре UNIX — tty. Затем ядро проверяет состояние tty. Предположим, tty находится в режиме обработки данных или в режиме icanon и ни один специальный символ не связан с х. На следующем шаге буква х помещается во входной буфер и в виде эха передается назад терминалу, вызывая отображение на экране символа х. Если же произошла ошибка и нужно было ввести букву с, то теперь придется ввести символ стирания (5.09) (обычно он появляется в результате нажатия клавиш [CTRL-h], а также клавиши [BACKSPACE] или [DELETE], которые могут иметь, а могут и не иметь один и тот же код, что зависит от конкретного терминала или клавиатуры). Код нового символа тоже будет послан tty. После этого ядро удалит из входного буфера последний символ. Поскольку таковым является строчная буква х (обычный печатаемый символ), ядро направит терминалу единственный код возврата на одну позицию ([BACKSPACE]) или последовательность [BACKSPACE] [ПРОБЕЛ] [BACKSPACE]. В результате курсор сместится на одну позицию назад, * В различных системах в "байте" последовательного порта используется от пяти до девяти битов. Однако это не должно беспокоить большинство пользователей. В большей части систем используется восемь битов: либо как семь битов данных и один бит четности, либо как восемь битов данных без бита четности. Таким образом, многие - пользователи могут игнорировать эти опции и выбирать лишь установку режима контроля четности. 648 Часть седьмая. Терминаяы и принтеры
41.02 а символ будет удален с экрана. (В системе POSIX того же результата можно достигнуть, установив режим "эхо".) Наконец, при нажатии клавиши [RETURN] или [ENTER] терминал посылает ASCII-код 13. Если установлена опция icml, ядро изменяет этот код на 10 (новая строка), который и возвращается терминалу. Если же установлена опция ocrnl, терминал получит оба кода — 13 (возврат каретки) и 10 (перевод строки). Ядро, восприняв код 10 как признак новой строки, завершает ввод и передает содержимое буфера с накопленными данными той прикладной программе, которая в текущий момент читает данные из tty. Если пользователь отменяет опцию icanon (или устанавливает опцию cbreak), ядро берет частично заполненный буфер и передает содержащиеся в нем символы прикладной программе. После этого ядро будет передавать прикладной программе каждый поступивший символ, продолжая в то же время посылать введенные данные назад на терминал. Если отменить опцию echo, ядро перестанет отображать данные на терминале. Так работают полноэкранные редакторы типа vi: они выключают опции icanon и echo, а также отменяют применение некоторых специальных символов. Впоследствии vi сможет реализовать собственное эхо. В этом случае, если вы нажмете клавишу [I], чтобы перейти в режим вставки, буква i не появится на терминале. Некоторые проблемы возникают при повторном включении опции icanon. В частности, вы могли вводить символы в то время, когда опция icanon была выключена. Символы поступают в tty, который упаковывает их и отсылает прикладной программе. Прикладная программа, возможно, еще не прочла их, но tty считает, что символы переданы. Поэтому пользователь может оказаться не в состоянии восстановить их для своей текущей входной строки. Более старые версии системы Berkeley UNIX способны справляться с этой проблемой, но версии System V, которые используют потоковый интерфейс, этого делать не могут. Хотя, если система достаточно быстрая, можно даже не заметить этого явления, поскольку прикладные программы всегда будут успевать включать опцию icanon еще до того, как пользователь сумеет что-то ввести с терминала. . . А как насчет символов табуляции? Еще одним сложным моментом является обработка символов табуляции. Для терминала каждого типа характерна своя модель поведения. Кроме того, у каждого пользователя свои соображения относительно того, как должны функционировать символы табуляции. Клавише табуляции соответствует ASCII-код 9, который служит командой переместить курсор вправо к следующей позиции табуляции. Но где находится эта позиция? Более того, если курсор был перемещен в эту позицию, то как ядро вернет его назад, если вы решите стереть символ табуляции? Во многих версиях UNIX ядру можно дать указание установить шаг табуляции. При решении этой задачи ядро устанавливает шаг табуляции, равный восьми символам. Это означает, что если на терминал выводятся символ новой строки, два обычных символа и символ табуляции, то последний преобразуется в шесть пробелов. Теперь при нажатии клавиши [BACKSPACE] курсор должен вернуться в ту позицию, в которой была нажата клавиша табуляции. Ядро Berkeley-систем обрабатывает такую ситуацию правильно. Однако в некоторых случаях могут возникнуть проблемы. Например, если tty настроен так, чтобы символы табуляции передавались без изменения, и если терминал сам устанавливает позиции табуляции на каждый десятый символ (именно такая установка применяется по отношению к терминалам DEC-10), то tty возвратит курсор на меньшее число позиций, чем необходимо. Даже если шаг табуляции равен восьми символам, tty и терминал могут по-разному определять текущую позицию курсора. Большинство Berkeley-систем, например, подсчитывают управляющие символы как "обычные" выводимые символы, независимо от того, влияют они на перемещение курсора или нет. Чтобы введенные управляющие символы не влияли на выполнение команды возврата на одну позицию, ядро Berkeley-систем представляет их в виде двухсимвольной последовательности. Например, символы [CTRL-g] обычно отображаются как AG. Стирание такого управляющего символа выполняется правильно: tty выдает два символа возврата на одну позицию, два пробела и еще два символа возврата на одну позицию. Стирание большого числа символов, среди которых, возможно, есть символ табуляции, будет Настройка терминала и последовательного порта 649
41.02 реализовано правильно. Этот режим, называемый режимом "эха управляющих символов", может быть установлен или отменен пользователем. Наряду с задержкой при возврате каретки, которая введена для того, чтобы дать время каретке телетайпа переместиться влево, в некоторых версиях UNIX делается задержка при вводе символа табуляции. Подобно своему аналогу, эта задержка также стала бесполезной и практически не используется. Стандарт POSIX предусматривает задержки обоих видов, но ни одна из них не является обязательной. О наличии задержки свидетельствуют поля сг2, сгЗ, tabl и т.п. в выводе команды stty. По умолчанию в вашей системе, скорее всего, установлен режим "нет задержки", и вряд ли кто-либо специально изменит этот режим. Параграф 41.04 содержит более подробную информацию об использовании символов табуляции. Управление попками Управление потоками, позволяющее избежать потери вводимых и выводимых символов, — это, возможно, одна из самых сложных задач. Большинство терминалов, выпущенных начиная с 80-х гг., поддерживают режим управления потоками XON/XOFF (при работе на высоких скоростях поддержка этого режима является обязательным требованием). Когда терминал не успевает отображать символы, он "просит" UNIX остановиться, посылая символ XOFF (ASCII-код 19 или [CTRL-s]). Если компьютер, работающий под управлением UNIX, не остановится достаточно быстро, часть текста будет потеряна. Когда терминал готов продолжить прием данных, он посылает символ "продолжить" — XON (ASCII-код 17 или [CTRL-q]). Разработчики этого механизма и не предполагали, насколько широкое распространение он получит (на одном из первых телетайпов указанные коды служили лишь для включения и выключения перфоратора бумажной ленты; свою значимость они приобрели только с повсеместным внедрением высокоскоростных каналов связи). К сожалению, большинство терминалов позволяют пользователям вводить коды [CTRL-s] и [CTRL-q], но не предоставляют метода, по которому сигнал терминала "остановить вывод" можно отличить от нажатия клавиш [CTRL-s]. Как следствие ведется беспрестанная борьба между пользователями, которые хотят применять клавиши [CTRL-s], и компьютерными системами, стремящимися самостоятельно распоряжаться этой возможностью. Другие системы, особенно производства фирмы Hewlett Packard, используют режим управления потоками ENQ/ACK. В таких системах терминал и главный компьютер должны заблаговременно согласовать минимальный размер буфера. Каждый участник обмена может послать только это оговоренное число символов, после чего он должен остановиться и ждать сигнала "продолжай". Чтобы запросить такой сигнал, необходимо отправить ASCII-код 5 или [CTRL-e]. Если система-получатель встречает среди данных код ENQ (enquire — запрос) и готова продолжать прием данных, она посылает подтверждение — ASCII-код 6 или [CTRL-f]. Данный метод превосходит метод XON/XOFF в одном отношении: в случае его применения никогда не возникает такой проблемы, как неспособность перегруженной системы немедленно прекратить вывод при получении соответствующей команды. Однако пользователям метода ENQ/ACK yk, возбраняется применять управляющие коды [CTRL-e] и [CTRL-f]. Кроме того, данный метод не реализован в большинстве UNIX-систем. Наиболее надежным методом управления потоками является передача вне диапазона (out of band). "Вне диапазона" означает, что пользователи не могут сымитировать этот метод с помощью символов управления. Управление вне диапазона реализуется в программах при условии, что используется нечто подобное режиму ENQ/ACK, а также применяются некоторые приемы кодирования. Но на практике в большинстве UNIX-систем, поддерживающих какой-либо режим управления потоками вне диапазона, осуществляется так называемое аппаратное управление потоками (режим управления потоками RTS/CTS). (Этот режим может быть реализован на многих системах без какого-либо специального оборудования, поэтому название в скобках лучше.) При использовании режима RTS/CTS в управлении потоками задействованы два провода, имеющиеся в кабеле последовательного порта, — RTS и CTS. (Провода RTS и CTS, названия которых означают "запрос на передачу" (Request To Send) и "можно посылать" (Clear To 650 Часть седьмая. Терминалы и принтеры
41.04 Send), изначально предназначались для использования с полудуплексными модемами. Поскольку в наши дни полудуплексные модемы стали музейной редкостью, это разумный подход, хотя он и противоречит стандарту RS-232.) Провод RTS, принадлежащий терминалу, перекрестно соединяется с проводом CTS, принадлежащим компьютеру, и наоборот. И терминал, и компьютер выдают сигнал RTS всякий раз, когда они готовы принимать данные, однако они не начинают передачу, пока не получат сигнал CTS. К сожалению, данный метод реализован в немногих системах, а там, где он есть, он не всегда реализован правильно.* Таким образом, несмотря на то что режим RTS/CTS обеспечивает высокую надежность, не следует ограничиваться только им. Однако стоит проверить, не обладает ли команда stty, применяемая в вашей системе UNIX, опцией rts/cts. Что дальше? Если все, о чем мы говорили, показалось вам необычайно сложным, остается лишь уповать, что вы никогда не столкнетесь с синхронной передачей данных, стандартом RS-422, DIN-разъемами, средствами защиты от удара молнии и многими другими аппаратными аспектами компьютерной связи. Обеспечение взаимодействия двух произвольно взятых компьютеров может оказаться чрезвычайно трудной задачей. И в этом случае на помошь приходят стандарты. Если все средства соответствуют стандартам POSIX и RS-232 (например, режим управления потоками RTS/CTS), все должно происходить без сбоев. - СТ 41.03 Опрецеление установок терминала с помощью команцы stty ^^^ш Вероятно, тем, кто обладает лишь базовыми знаниями, трудно согласиться с утверждением о l f#i 1 целесообразности следовать предписаниям научного "трактата" Криса Торека, повествующего ^._^ ° принципах работы команды stty (41.02). Но эта книга предназначена для начинающих в такой stty же мере, как и для тех, кто знает все. :-) (51.12) [Великолепная мысль, Тим. Здесь же удобно разместить пиктограмму компакт-диска для GNU-версии команды. ;Л) — JP] Поэтому: • чтобы выяснить, каковы установки для терминала в данный момент, введите % stty • чтобы получить более полный листинг, наберите в System V команду % stty -a • а в BSD команду % stty -everything Как указывал Джерри Пик в примечании к параграфу Криса, обязательно имейте под рукой интерактивное руководство по команде stty. - TOR 41.04 Как UNIX обрабатывает символы табуляции Символы табуляции используются довольно широко: в таблицах, в параграфах с отступом, в исходных текстах программ, в выводе многих программ и т.д. В отличие от других операционных систем, UNIX достаточно гибко обращается с символами табуляции. Большинство программ, принтеров и терминалов UNIX настроены так, что шаг табуляции составляет восемь символов. Если терминал или принтер получает символ табуляции, происходит переход в следующую позицию табуляции: 9-ю, 17-ю, 25-ю и т.д. UNIX (ядро и * Например, на рабочих станциях Sun метод ЯУЗ/Ста поддерживается аппаратно, но специальная микросхема Zilog, применяемая для этих целей, использует для контроля приема данных линию DCD (Data Carrier Detect — обнаружение несущей). Таким образом, введя stty crlscls, вы не сможете дать модему указание набрать номер, поскольку сигнал на линии DCD отсутствует. Обойти эту проблему можно лишь за счет управления как оборудованием, так и ядром UNIX. Настройка терминала и последовательного порта 651
41.04 драйверы устройств (42.oi)) обычно не интерпретирует символы табуляции и не устанавливает шаг табуляции. Система обрабатывает эти символы так же, как и любые другие символы, передавая их утилитам или устройствам, например терминалу. Шаг табуляции можно варьировать. Когда я пишу программы, я делаю отступ размером 4 символа, поскольку отступы в 8 символов занимают слишком много места. Для того чтобы менять шаг табуляции, необходимо знать, как система обращается с символами табуляции. Интерпретация символов табуляции При нажатии клавиши [TAB] посылается отдельный символ табуляции. Если редактируется файл, то редактор, скорее всего, поместит этот символ в файл. Впоследствии при выполнении команд cat (25.02), gr (4з.о?), fe (4з.щ и т.д. каждый символ табуляции будет читаться и посылаться на терминал, принтер или другое устройство. Обычно символ табуляции интерпретируется самим устройством. А вообще-то он такой же символ, как и все остальные (см. описание команды stty -tabs, которая рассматривается ниже). Если терминал имеет режим настройки, перейдите в этот режим и узнайте шаг табуляции. Скорее всего, установлены такие позиции табуляции: 9, 17, 25 и т.д. Когда терминал получает от UNIX символ табуляции, он перемещает курсор в следующую позицию табуляции. Для терминала могут быть заданы и другие позиции табуляции, например: 11, 21, 31 и т.д. Предположим, к некоторому файлу применена команда cat. У программистки, которая создала этот файл, по умолчанию был установлен шаг табуляции размером 8 символов. Когда эта дама вводила содержимое файла, она использовала символы табуляции, чтобы создать отступы для строк (в данном примере символы табуляции обозначены символом ►). На ее терминале файл выглядел следующим образом: % cat prog while read line; do ^- set $line b- for word in line; do »- ► case."$l" in Если на терминале шаг табуляции равен 12 символам, файл выглядит так: % cat prog while read line; do b- set $line ► for word in line; do ► ► case "$1" in Ни ядро UNIX, ни программа cat ничего не делали с файлом prog.. Просто терминал иначе интерпретировал символы табуляции. Допустим, необходимо вывести файл на терминал (или отредактировать файл на терминале), для которого задан иной шаг табуляции, чем у того терминала, что используется в данный момент. Какие действия следует выполнить в таком случае? ^^^_ • Используйте утилиту UNIX, которая преобразует символы табуляции в пробелы. В системе I (Л) 1 BSD такую работу выполняет команда expand. В этом случае символы табуляции не L^.J попадут на терминал, а позиции табуляции не будут учитываться: expand % expand prog while read line; do set $line for word in line; do case "$1" in В System V выполните команду рг с опциями -t и -е: % pr -t -* prog while read line; do 652 Часть седьмая. Терминалы и принтеры
41.05 set $line for word in line; do case "$1" in В результате файл будет иметь надлежащий вид, независимо от того, какой шаг табуляции установлен на данном терминале. Чтобы изменить шаг табуляции, задайте соответствующую опцию. Например, установить шаг в 4 символа позволяет команда expand -4 или рг -I -е4. • Укажите своему текстовому редактору использовать другой шаг табуляции. Редактор, скорее всего, преобразует символы табуляции в пробелы перед тем, как выводить их на терминал. Например, в редакторе vi введите команду :set tabstop=4 чтобы в редактируемой программе шаг табуляции составлял четыре символа. С этого момента файл prog будет выглядеть следующим образом, хотя символы табуляции останутся на том же месте: % vi. prog while read line; do ^ set $line ^ for word in line; do V ► case "$1" in Меняйте шаг табуляции непосредственно в процессе отображения файла, если, конечно, ваш текстовый редактор предоставляет такую возможность. Файл с нестандартным шагом табуляции следует снабжать комментарием, чтобы пользователи знали об этой установке. В этом случае можно также преобразовать символы табуляции в пробелы перед сохранением файла. Следующий пример показывает, как преобразовать символы табуляции в пробелы в системе, где есть редактор vi и команда expand и где шаг табуляции составляет четыре символа: :%\ 30.22 : % ! expand -4 Командой, противоположной команде expand, является, разумеется, команда unexpand (24.06). Она преобразует пробелы в символы табуляции, исходя из того, что шаг табуляции равен 8. Преобразование символов табуляции в пробелы Обычно ядро UNIX и драйверы устройств не преобразуют символы табуляции в пробелы. Впрочем, иногда, когда какая-нибудь необычная установка для шага табуляции вызывает проблемы, можно указать драйверу устройства выполнить подобное преобразование. Для этого предназначена следующая команда: % stty -tabs Теперь в большинстве случаев UNIX не будет посылать терминалу символы табуляции. — JP 41.05 Почему в некоторых системах курсор можно свободно перемещать по символам приглашения [Пользователи заметили одну странную особенность: в BSD невозможно выполнить возврат на одну позицию в приглашении интерпретатора shell, а в System V при нажатии клавиши [BACKSPACE] можно переместиться к самому краю экрана или окна, стирая приглашение. В настоящем параграфе приведено объяснение Брэндона Оллбери. — TOR] С помощью команды stty old можно заставить BSD-систему действовать так же, как System V. Но я не думаю, что вы этого хотите... Настройка терминала и последоаатепьноп порта 653
41.06 Драйверу терминала (42М) System V ничего не известно о конфигурации терминала, следовательно, в этой системе не предпринимаются попытки представить, как будет выполняться отображение введенной информации. Временами такое поведение системы раздражает, но в нем нет ничего неожиданного. В драйвере терминала (try) системы BSD предполагается, что терминал ведет себя "разумно", что управляющие последовательности не таят в себе неожиданностей и т.д. Поэтому данная система позволяет успешно решить проблему, связанную с установкой курсора в начале строки приглашения. В BSD драйвер терминала отображает управляющие символы в формате "со стрелкой вверх" [например, ЛА для [CTRL-a] — JP]. Кроме того, предполагается, что для терминала выполнена команда stty с правильной опцией (stty tabs применяется, если терминал имеет 8-символьную аппаратную установку шага табуляции, иначе используется команда stty -tabs). Однако для достижения согласованности с необычными терминалами драйверу терминала требуются другие ухищрения (такие как команда stty tilde, позволяющая применять символ ~ вместо ASCII-символа ESC на старых терминалах фирмы Hazeltine). Хотя в большинстве случаев применение методов, характерных для BSD, оказывается успешным, они не лишены недостатков. Приведем пример. Мне приходилось устанавливать на некоторых DEC-совместимых терминалах режим отображения управляющих символов (41.09), чтобы заставить терминал вместо графических выводить ASCII-символы. System V обеспечивает удобный способ решения этой задачи: достаточно указать управляющий символ в командной строке интерпретатора shell, вернуться на одну позицию назад и нажать клавишу [RETURN], чтобы вывести корректное приглашение интерпретатора shell, а не что-то непонятное на языке вроде греческого. В BSD я обращался для этого к команде cat (25.02). Кроме того, хотя BSD и отображает управляющие символы в формате "со стрелкой вверх", этот метод оказывается малоэффективным, если какая-нибудь фоновая программа начинает выводить на терминал управляющие символы. (Да, вывод можно прервать командой stty tostop (12.07), но иногда подобные действия фоновой программы являются желательными, поскольку позволяют определить ее состояние.) Программы типа ksh и tcsh тоже выполняют "разумное" отображение. При их использовании возникает дополнительная проблема: драйвер терминала системы BSD может выдать сообщение, когда какая-нибудь программа выполняет вывод на терминал во время чтения буфера строк и автоматически повторно выводит строку после очередного нажатия клавиши. Однако в программе пользователя невозможно выявить такой случай, поэтому может возникнуть ситуация, когда, несмотря на любые попытки, не удается избежать повторного вывода строк. По крайней мере, создатели System V ведут себя честно, ничего не обещая. — ВА, из телеконференции comp.unix.misc в Usenet, 12 октября 1991 г. 41.06 Использование команды sleep цля защиты установок порта от изменения Иногда необходимо использовать команду stty (4Ш), чтобы изменить установки для последовательного порта, отличного от того, с которым связан драйвер терминала (например, для порта принтера или модема). Но без программы, которая использует порт, такие установки во многих версиях UNIX бесполезны. Драйвер последовательного порта, не связанный с каким-либо процессом, обычно повторно инициализируется значениями по умолчанию каждый раз, когда пользователь обращается к нему. Единственный способ выполнить для драйвера какие-либо установки и сделать их постоянными на какой-то период — обратиться к порту и занять его на некоторое время, а затем установить для него нужные режимы. Это осуществляется следующим образом (перед выполнением установок): % sleep 1000000 > /dev/ttyXX S Впоследствии, по завершении всего комплекса работ, процесс sleep (40.02) можно уничтожить (Ж 10). — СТ, из телеконференции net.unix в Usenet, 30 января 1984 г. 654 Часть седьмая. Терминалы и принтеры
41.08 41.07 Чтение с терминала оч-ч-чень длинных строк Иногда входные данные могут содержать очень длинные строки, которые необходимо записать в файл. Возможно, эти строки поступили с персонального компьютера, с устройства, которое подключено к терминалу, или же представляют собой длинную последовательность символов, которую нужно ввести с клавиатуры. Обычно драйвер терминала (42.oi) UNIX принимает все вводимые символы, пока не встретит признак конца строки или символ прерывания. В большинстве буферов зарезервировано место для 256 символов. Если символы вводятся с клавиатуры, эту проблему легко устранить: приблизительно после каждых 200 символов вводите управляющий символ [CTRL-d], чтобы очистить входной буфер. После этого вы не сможете выполнить возврат к символам, которые расположены перед точкой, но интерпретатор shell прочитает все, что было введено. Можно также указать UNIX передавать каждый введенный символ без буферизации. Используйте команду stfy (4Ш), чтобы установить для терминала режим ввода cbreak (или неканонический режим). Например: fc stty cbreak % cat > file введите очень длинную строку... |CTRL^cl % stty -cbreak В System V укажите команду stty -icanon в начале этой последовательности и команду stty icanon в конце. В режиме cbreak коды, полученные в результате нажатия специальных клавиш типа [BACKSPACE] и [DELETE], не обрабатываются: они просто сохраняются в файле. Нажатие клавиш [CTRL-d] не приведет к завершению работы команды cat. Для этого следует ввести обычный код прерывания, нажав, к примеру, клавиши [CTRL-c]. (Если вы случайно нажали клавишу [BACKSPACE] или [RETURN], то сможете увидеть эти символы в файле, просмотрев его посредством команды gd (25.07) с опцией -с. Отфильтруйте эти символы с помошью команды tr -d (Js.it> или текстового редактора (редактор GNU Emacs (.12.01) способен обрабатывать очень длинные строки).) И еше одна проблема. Использование интерпретатора shell со встроенной функцией редактирования командной строки (п.н) и (или) дополнения имени файла (9.0S) может быть сопряжено с рядом проблем, поскольку такое редактирование выполняется посредством команд типа stty. В таком случае запустите простой интерпретатор Bourne shell (введите sh или /bin/sh) перед тем, как ввести команду stty. -JP 41.08 Псевцотерминалы и оконные системы Когда появились оконные системы, поставщикам UNIX пришлось искать способы выполнения в окнах программ, написанных для терминалов, которые работают с ASCII-кодами. Решение заключалось в оснащении оконной системы эмулятором терминала — программой, которая позволяла бы работать с окном как с терминалом. Большинство эмуляторов имитировали либо терминал VT100 фирмы DEC, либо терминал VT102 той же фирмы. Они реагировали на управляющие последовательности и довольно успешно выдавали себя за соответствующий терминал. С другой стороны, системе необходимо было знать, с каким "терминалом" имеет дело какая-либо программа, чтобы позволить ей читать входные данные и передавать полученные результаты туда, куда надо. Обычно между файлом tty и физическим устройством, подключенным к последовательному порту, существует связь. Но какая может существовать связь с областью на экране рабочей станции — псевдоустройством, не имеющим ничего общего с последовательным портом? Ответ был получен с появлением в системе устройства, выполняющего функции настольного терминала. Оно получило название pty — псевдотерминал. — TOR Настройка терминала и последовательного порта 655
41,09 41.09 Команды для настройки терминала Большинство терминалов и оконных систем читают каждый символ, который им посылает главный компьютер. Они следят за управляющими последовательностями — сериями символов, которые представляют собой команды для терминала или окна. (Я буду говорить просто "терминал", но материал настоящего параграфа применим и к окнам.) Когда терминал обнаруживает управляющую последовательность, он, вместо того чтобы отображать на экране символы, выполняет заданную команду. Пользователь может самостоятельно посылать управляющие последовательности из командной строки. Предположим, какая-то программа оставила ваш терминал в режиме негативного изображения. Если терминал работает по стандарту ANSI (как, например, VT100), можно ввести команду echo (s.oe) и послать последовательность Л [ [От (где Л [ — это символ ESC), чтобы выключить этот режим. Однако проще создать отдельный сценарий. Управляющие последовательности должны быть описаны в документации по вашему терминалу. В этом параграфе я буду использовать управляющие последовательности для терминала VT102 или совместимого с ним терминала. Чтобы создать общие команды настройки, которые должны работать и с другими терминалами, следует воспользоваться командой tput или tcap (41.10) и прочитать информацию о терминале, содержащуюся в базах данных terminfo и termcap. Предположим, вы решили, что какой-то сложный вывод на экран проще прочитать в режиме негативного изображения. Перевести терминал в этот режим и впоследствии вернуться в нормальный режим позволяет такой алгоритм: % Rewid % ...введите команды/ любой текст будет отображаться негативно... % Normal % ... теперь все будет отображаться обычным образом... Большинство полноэкранных программ заново инициализируют терминал. При этом могут быть отменены некоторые установки, выполненные этими командами. Чтобы написать сценарий для смены режима терминала, создайте файл, например, с именем Clear. В первой строке сценария в переменной интерпретатора shell (6.08), которая называется е, запоминается символ ESC. Эта переменная применяется в остальных командах сценария и обозначается как $ {е}: С) Л *■ /bin/sh # ПОСЛАТЬ ТЕРМИНАЛУ VT102 ИЛИ СОВМЕСТИМОМУ С НИМ ТЕРМИНАЛУ УПРАВЛЯКЩуТО ПОСЛЕДОВАТЕЛЬНОСТЬ Clear ccho.3' 45.35 e="~echo e I tr e '\033'4'* # Запомнить символ ESC case "$0" in *Clear) seq="${e) (;H$(e) (2J" ;;# переместиться в левый верхний угол, очистить экран # СМЕНИТЬ НАБОР СИМВОЛОВ. КОМАНДА "NOG" НЕОБХОДИМА, # КОГДА ТЕРМИНАЛ СЛУЧАЙНО ПЕРЕКЛЮЧАЕТСЯ В РЕЖИМ ГРАФИКИ: *NOG) seq="$(e}(В" ;; # отменить графику *Graphics) seq="$(e}(0" ;; # строчные буквы выводятся в графическом режиме # ПРИМЕЧАНИЕ: СЛЕДУЮЩИЕ УСТАНОВКИ НЕ БУДУТ КОРРЕКТНЫМИ ДЛЯ ПОЛНОЭКРАННЫХ # ПРОГРАММ ТИПА vi. # ЛУЧШЕ ПЕРЕУСТАНОВИТЬ ПАРАМЕТРЫ СВОЕГО ТЕРМИНАЛА (tset, stty) : *С132) seq="$(e)t?3;h" ;; # 132-колоночный режим *С80) seq="$(е)[?3;1" ;; # 80-колоночный режим *Rewid) seq="$ (e} [?5;h" ;; # режим негативного изображения *NOrmal) seq="${e)[?5;1" ;; # режим нормального изображения 656 Часть седьмая. Терминалы и принтеры
41.10 # ЗАПИСАТЬ СООБЩЕНИЕ В СТРОКУ СОСТОЯНИЯ ТЕРМИНАЛА (УДОБНО ДЛЯ НАПОМИНАНИЯ) # ПРИМЕР: ToStatus Очистите свои файлы! # И ОЧИСТИТЬ ЕЕ. %*44.15 *ToStatus) seq="$ (e } 7$ {е} [25; If $ {е( [ОК$*$ {е } 8" ;; *ClrStatus) seq="${e}7${e}[25;If${e([OK$(e)8" ;; *) echo "$0: HELP - can't run myself." 1>&2; exit 1;; esac # ПЕРЕДАТЬ $seq НА ТЕРМИНАЛ БЕЗ ИНТЕРПРЕТАЦИИ КОМАНДОЙ echo В SYSTEM V: cat << END_OF_seq $seq END_OF_seq exit 0 Этот сценарий можно инсталлировать с компакт-диска или из сетевого источника (52.07). Если вы не копируете файл с диска, будьте внимательны, чтобы в точности воспроизвести приведенные в сценарии управляющие последовательности. В режиме Graphics используется цифра 0, а не буква О. В режимах ToStatus и ClrStatus используется цифра 1 (один), а не буква 1 (L). Если вы извлекаете сценарий из архива, дайте указание команде tar установить команду Clear и восемь ссылок на нее: % tar jcvf архив, tar Clear NOG Graphics C132 \ C80 Rewid Normal ToStatus ClrStatus x Clear, 1371 bytee, 3 tape blocks NOG linked to Clear Graphics linked to Clear В переменной $0 сценарий проверяет имя, под которым он был вызван (44.22), чтобы определить, какую команду выполнить (звездочка соответствует любому путевому имени перед названием команды). Этот прием позволяет сэкономить место на диске. Можно также добавить другие команды и ссылки, дописав строки в операторе case. — JP, за основу взяты идеи Брюса Барнетта 41.10 Использование возможностей базы данных terminfo в программах интерпретатора shell Недостаток метода, описанного в параграфе 41.09, состоит в том, что он предполагает применение жестко закодированных управляюших последовательностей конкретного терминала. Если вы используете терминалы нескольких типов, для каждого из них придется создать различные команды смены режимов терминала. Именно для решения подобных проблем разработаны базы данных termcap и terminfo. НЭти базы содержат список свойств (характеристик) (4i.ii) терминалов каждого типа. Программа tput (стандартная во всех системах, использующих базу данных terminfo) позволяет выводить значение любого свойства. Программа tcap применяется для той же цели в системах, в tcap которых используется база данных termcap. (Программа tcap изначально называлась tc. Мы переименовали ее, чтобы избежать конфликтов с программой tc — интерпретатором ditroff-макросов для терминалов Tektronics 4015.) Благодаря этим программам становится возможным применение таких свойств терминала, как инверсное отображение символов. Например, чтобы выделить приглашение командной строки, выводимое интерпретатором shell, нужно применить следующие операторы, в которых вызывается программа tput. # Запомнить в переменной HIGHLIGHT значение свойства из базы данных terminfo, # предназначенного для включения режима выделения; # изображение может быть выделено полужирным шрифтом или инверсно HIGHLIGHT^tput sraso4 Настройка терминала и последовательного порта 657
41.11 # Запомнить в переменной NORMAL значение свойства из базы данных terminfo, # предназначенного для выключения режима выделения NORMAL='tput rmso' # Вывести приглашение с выделением ес1ю...\с 8.06 echo "${ HIGHLIGHT) Press RETURN to accept value: $(NORMAL}\c" Характеристики режима отображения (например, координаты курсора) задаются значениями, которые в командной строке указываются после названия характеристики. Например, чтобы выдать последовательность для перемещения курсора в верхний левый угол экрана (строка О, колонка 0), следует ввести такую команду: $ tput cup О О Рассмотрим еше одну возможность применения программы tput. Предположим, на экран случайно послана последовательность команд, которая исказила вывод или установила режим выделения символов, затрудняющий их восприятие. Такое иногда случается, когда пользователь читает нетекстовый файл или почтовое сообщение, содержащее управляющие символы или заканчивающееся ненужными данными. Причина может также состоять в том, что на терминал послана последовательность, устанавливающая альтернативный набор символов, после чего информация на экране становится непригодной для чтения. Существует два способа восстановления обычного набора символов: повторная загрузка терминала и ввод команды tput init ts.n). Более эффективным является использование программы tput. — JS, TOR, из книги termcap & terminfo издательства O'Reilly & Associates 41.11 Описания терминалов в базах данных termcap и terminfo Свойства терминалов описываются в базах данных termcap и terminfo (S.02) с использованием малопонятного, но зато компактного языка. В настоящее время рынок терминалов, работаюших с ASCII-кодом, остановился в развитии и четко стандартизирован. Поэтому сейчас язык не столь важен, как в те времена, когда он применялся для записи данных о новых терминалах. Однако еще возникают ситуации, когда требуется прочитать данные о терминалах. Например, некоторые свойства терминалов используются в программах интерпретатора shell (4i.io) или в переназначениях функциональных клавиш (4i.iv. Невозможно дать в книге подробный список всех свойств терминалов. Вы найдете его в интерактивном руководстве по базам данных termcap и terminfo вашей системы. Однако мы хотим познакомить вас с языком этих баз данных. В данный параграф включена упрощенная запись для терминала Wyse-50 фирмы Wyse Technology. Свойства, которые здесь описаны, — это лишь подмножество, достаточное для ознакомления с базовым синтаксисом языка: # Фрагмент записи базы данных termcap для терминала WY-50 фирмы Wyse Technology n9|wy50IWY50| Wyse Technology WY-50:\ :bs:am:co#80:li#24:\ :up=AK:cl=AZ:ho=AA:nd='-L:cm=\E=%+ %+ : А вот соответствующая запись из исходного файла terminfo: # Фрагмент записи базы данных terminfo для терминала WY-50 фирмы Wyse Technology wyS0|WYS0|Wyse Technology WY-50, am, cols#80, lines#24, cuul=AK, clear=*Z, поте=ЛЛ, cufl="L, cup=\E=%pl%'\s'%+%c%p2%■\s'%+%c, Обратная косая черта употребляется для подавления символа новой строки. Запись базы данных termcap должна быть единой логической строкой, в которой для разделения полей используется двоеточие. В базе данных terminfo не требуется, чтобы запись представляла собой единую строку, поэтому обратная косая черта в этих записях не ставится. Здесь разделителями служат запятые. 658 Часть седьмая. Терминаяы и принтеры
41.11 Несомненно, язык баз данных не отличается многословностью! Однако если работать с ним методически, в нем начинает просматриваться смысл. В базах данных termcap и terminfo существует три типа строк: строки комментариев, строки со списком альтернативных имен терминала и строки, в которых определяются свойства терминала. • Строки комментариев В приведенных выше двух записях для баз данных termcap и terminfo первые строки являются строками комментариев. # Фрагмент записи базы данных termcap для терминала WY-50 фирмы Wyse Technology # Фрагмент записи базы данных terminfo для терминала WY-50 фирмы Wyse Technology В начале каждой строки комментария стоит знак диеза (#). Создание вложенных комментариев недопустимо: строка является либо комментарием, либо частью записи. В базах данных termcap и terminfo существует соглашение, в соответствии с которым записям с данными о терминале должны предшествовать комментарии. • Строки с именами Вторая строка — это список альтернативных имен терминала, разделенных вертикальной чертой. n9|wy50IWY50I Wyse Technology WY-50:\ ...termcap wy50|WY50IWyse Technology WY-50, ...terminfo Альтернативные имена введены для удобства. Любое альтернативное имя может быть присвоено переменной среды TERM (S.io). В соответствии с соглашением последним в списке стоит полное название терминала. Список альтернативных имен — это первое поле в описании терминала. Он заканчивается либо двоеточием (база данных.termcap), либо запятой (база данных terminfo). За списком альтернативных имен следует список свойств терминала. Список свойств может располагаться непосредственно рядом со списком альтернативных имен, но запись намного легче читать, если альтернативные имена перечислены в одной строке, а свойства — в следующей. Когда исходный файл базы данных terminfo обрабатывается с помощью программы tic, полученные данные помещаются в файл, имя которого соответствует первому альтернативному имени (в данном случае — /usr/lib/terminfo/w/wy50). Для остальных альтернативных имен, за исключением последнего, создаются ссылки на этот файл. В данном примере для обеспечения доступа к описанию терминала переменной TERM может быть присвоено либо значение wy50, либо WY50. • Список свойств Остальные строки из записи базы данных содержат список действительных свойств терминала. Данные строки набирают с отступом (отступ образуется посредством символа табуляции или пробелов), чтобы выделить их на фоне строк с альтернативными именами. Отметим, что отступ для строк со свойствами — это не прихоть, а одно из требований синтаксиса. В базе данных termcap отдельные свойства распознаются по двухсимвольному названию. В базе данных terminfo названия свойств могут содержать от двух до пяти символов. Название свойства является первой частью каждого поля. Существует три типа свойств: • Булевы свойства. В базе данных такое свойство представлено в виде названия без аргумента. Например, свойство am (как в termcap, так и в terminfo) указывает, что терминал автоматически выравнивает границы строк, перенося курсор в начало следующей строки, когда он достигает последней позиции текущей строки. Если свойство am не установлено, программы предполагают, что терминал не обладает этим свойством. Свойство am — это одно из булевых свойств, которое фиксирует наличие возможности. Булевы свойства используются также для указания отсутствия возможности. Например, Настройка терминала и последовательного порта 659
41.11 если терминал не обрабатывает должным образом символы новой строки, может возникнуть так называемый "сбой новой строки". В таком случае необходимо определить свойство хп (termcap) или xenl (Jerminfo), которое служило бы программам указанием учитывать данную особенность терминала. • Числовые свойства. В базе данных такое свойство представлено в виде названия, за которым следуют знак диеза и числовое значение. Например, свойство со#80 (termcap) или cols#80 (terminfo) указывает, что терминал имеет 80 колонок. Значение числового свойства не может быть отрицательным. • Строковые свойства указывают, как следует передавать команду терминалу. Формат строкового свойства таков: название, знак равенства, командная последовательность. Например, запись ир=ЛК (termcap) или cuul=AK (terminfo) говорит, что последовательность [CTRL-k] предназначена для перемещения курсора на одну строку вверх. Надеемся, что следующий пример, рассчитанный на терминал Wyse-50, внесет некоторую ясность. Сначала приведем вариант для базы данных termcap: ВО колонок Автоматическое выравнивание границ 24 строки :bs:am:co#80:li#24:\ :up=AK:cl=A Z:ho=A A:nd=AL:cm=\E=%+ %+ : Переместить курсор вверх АЛ Переместить курсор в начало экрана Код абсолютного перемещения курсора Очистить экран "L Переместить курсор вправо Рис. 41.1. Фрагмент записи базы данных termcap А вот вариант для базы данных terminfo: 80 колонок Авшометическое выравнивание границ 24 строки Переместить курсор вверх Очистить экран am, cols#80, lines#24, cuul=AK, clear=AZ, home=AA, cufl=AL, cup=\E=%pl%'\s'%+%c%p2%'\s ' %+%c , Переместить курсор в начало экрана Переместить курсор вправо Код абсолютного перемещения курсора Рис. 412. Фрагмент записи базы данных terminfo 660 Часть седьмая. Терминалы и принтеры
41.11 Два первых свойства в записи базы данных termcap и первое свойство в записи базы данных terminfo являются булевыми. bs Это свойство определяет возврат на одну позицию, когда терминалу посылается символ [CTRL-h] (Ан). Оно не имеет аналога в базе данных terminfo, в связи с чем считается устаревшим в базах terminfo и termcap системы BSD 4.3. Вместо данного свойства в базе данных terminfo следует явно определить комбинацию клавиш [CTRL-h] как управляющий символ для перемещения курсора влево (cull=AH). am Это свойство задает автоматическое продолжение вывода в следующей строке, am когда текущая строка достигает правого края экрана. Следующие два свойства являются числовыми. со#80 cols#80 Указывает, что терминал имеет 80 колонок. li#24 lines#24 Указывает, что терминал имеет 24 строки. Размер экрана 80 символов на 24 строки является наиболее распространенным. Существуют и другие размеры. Восемьдесят символов для ширины экрана когда-то были выбраны по той причине, что таковой является ширина перфокарты. Ну а высоту экрана сделали равной 24 строкам для того, чтобы юспользоваться преимуществами недорогой технологии изготовления телевизионных экранов. Остальные поля записи для терминала Wyse-50 содержат строковые свойства. Первые четыре довольно просты: ир=ЛК Свойство "переместить курсор вверх". Означает, что для перемещения курсора сии1=ЛК на одну строку вверх следует послать терминалу символ Ак. cl=AZ Свойство "очистить экран". Означает, что для очистки экрана следует послать clear=AZ терминалу символ "Z. Ьо=ЛЛ Свойство "переместить курсор в начало экрана". Означает, что для переме- home=AA щения курсора в начало экрана (верхний левый угол) следует послать терминалу символ АА ([CTRL-4). nd=AL Свойство "переместить курсор вправо без разрушения информации". Означает, cufl=AL что для перемещения курсора на одну позицию вправо без изменения текста следует послать терминалу символ Ль. Коды специальных символов Несомненно, символы ЛК, AZ, АА и AL вам знакомы. Символ А, за которым следует буква, — это условное обозначение непечатаемых управляющих символов, которые генерируются при одновременном нажатии клавиши [CONTROL] ([CTRL]) и еще одной клавиши. Обратите внимание на то, что в описании терминала управляющие символы вводятся в виде комбинации двух символов — символа Л и буквы. Настоящие управляющие символы не должны присутствовать в описании терминала. В базах данных termcap и terminfo для обозначения непечатаемых символов используются и другие коды. Заодно употребляются символы, которые в синтаксисе баз данных termcap и terminfo имеют специальное назначение. Эти дополнительные символы, с большей частью которых должны быть знакомы программисты, пишущие программы на С, приведены в табл. 41.1. Настройка терминала и последовательного порта 661
41.12 Таблица 41.1. Обозначения специальных символов в базах данных termcap и terminfo Обозначение \Е Лх \п \t \Ъ \1 \ххх \041 \072 \200 \0 \л \\ \, \: Описание Символ Escape Символ Escape [CTRL-x] символ новой строки символ возврата каретки символ табуляции символ возврата на одну позицию символ прогона страницы пробел символ перевода строки восьмеричное значение ххх восклицательный знак двоеточие нуль нуль знак вставки обратная косая черта запятая двоеточие Комментарий termcap и terminfo только terminfo где х — любой символ , только terminfo только terminfo необходимы три символа используется при работе с перечнем ранее введенных команд интерпретатора С shell в базе данных termcap обычное двоеточие используется в качестве разделителя комбинацию \000 нельзя употреблять для обозначения нуля только terminfo только terminfo только terminfo только terminfo только terminfo Кодирование аргументов Наиболее сложными являются последние свойства в обеих записях. Свойства cm= {termcap) и cup= (terminfo) определяют, как перемещать курсор в указанную позицию. Поскольку требуемая позиция указывается программой во время выполнения, данное свойство должно предоставлять механизм передачи аргументов. Программа использует описания этих свойств для определения вида строки, которую необходимо послать терминалу, чтобы переместить курсор в требуемую позицию. Поскольку мы не рассказываем, как составлять записи для баз данных termcap и terminfo, a только сообщаем, как их читать, вам необходимо знать лишь то, что знак процентов используется для кодирования параметров. Если он находится в записи базы данных, значит, в свойстве используются параметры времени выполнения. — JS, TOR, из книги termcap & terminfo издательства O'Reilly & Associates 41.12 Определение символов, которые генерируются специальными клавишами терминала При переназначении клавиш редактора vi (зш, зиз) может возникнуть необходимость определить, какой символ генерируется той или иной клавишей (например, клавишами со стрелками, функциональными клавишами и т.д.). Подобную информацию можно найти в документации по терминалу. Если же у вас ее нет, прочтите записи баз данных termcap и terminfo (4i.li). К сожалению, записи не всегда бывают полными: некоторые возможности терминала могут быть не описаны в записях о нем. 662 Часть седьмая. Терминапы и принтеры
41.12 В любом случае, если вы не знаете хорошо синтаксис баз данных, описывающих терминалы, вам будет трудно найти нужную информацию. Тем не менее, существует несколько оперативных способов, позволяющих выяснить, какой символ генерируется специальной клавишей. Каждый способ имеет свои недостатки, но среди них всегда найдутся такие, которые помогут определить, что же генерирует клавиша. • В редакторе vi установите режим вставки и воспользуйтесь управляющей последовательностью *У (.им), чтобы указать специальную клавишу. Т.е. нажмите клавиши [CTRL-v], а затем ту клавишу, о которой вы хотите получить информацию. На экране появится изображение соответствующего символа. Этот способ окажется бесполезным, если клавиша генерирует последовательность, содержащую более одного непечатаемого символа, поскольку команда ЛУ оказывает воздействие только на первый непечатаемый символ. Данный способ не годится и в том случае, если используются клавиши, которые генерируют символ новой строки (например, клавиша с изображением стрелки вниз на клавиатуре терминала Wyse-50). • В командной строке введите: stty4I.03 % stty -echo; cat -v; stty echo Затем нажмите специальные клавиши, сопровождая нажатие каждой из них переводом строки. Команда cat -v (25.07) отобразит на экране результат трансляции каждой клавиши. Завершив исследование специальных клавиш, нажмите [CTRL-d]. • В ответ на приглашение интерпретатора shell нажмите специальную клавишу, которая вас интересует. Если клавиша не имеет специального назначения, на терминале будет выполнена та команда, которая связана с клавишей. В зависимости от используемой версии UNIX, одновременно с выполнением команды может выводиться ее символьное изображение. (Не забывайте, что при дуплексной связи символы, вводимые с клавиатуры, не посылаются прямо на терминал, а сначала направляются системе, которая затем отображает их на экране.) В некоторых системах управляющий символ во входных данных распознается и отображается двумя различными способами: как печатаемое представление управляющего символа (например, Лг) и как действительный управляющий символ. В других системах отображается только действительный управляющий символ. А это означает, что вы видите действие, но не видите символа, который вызвал это действие. В любом случае интерпретатор shell сразу после нажатия клавиши [RETURN] выдает сообщение об ошибке, содержащее введенные символы. Сообщение может иметь следующий вид: *[[U : Command not found Если же клавиша генерирует последовательность, которая для устройства имеет специальное назначение, будет выполнена некоторая стандартная функция (например, прерывание, окончание файла или приостанов задания). Определить, какими командами вызываются эти функции, позволяет команда stt£ (4i.o3). — JS, TOR, из книги termcap & terminfo издательства O'Reilly & Associates Настройка терминала и последовательного порта 663
42 Проблемы с терминалами 42.01 Механизм вывода на экран терминала Сидя перед терминалом, бывает трудно представить, что находишься лицом к лицу почти с двадцатью пятью годами аккумулированной истории, результатами упорного труда и многолетней эволюции аппаратного обеспечения. При вводе данных посредством терминала мы имеем дело с четырьмя объектами: 1) с интерпретатором shell, утилитой или прикладной программой, которые интерпретируют вводимые символы и выполняют ряд действий, зависящих от результатов интерпретации; 2) с ядром UNIX, точнее, с драйвером последовательного порта, который по отношению к вводимым данным может выполнять некоторые операции преобразования низкого уровня; только после этого данные передаются программе, с которой, как кажется пользователю, он взаимодействует; 3) с терминалом, для которого характерна особая манера поведения: он может сам интерпретировать некоторые введенные пользователем данные и реагировать на них, может передавать все данные системе, а может выполнять и то и другое одновременно; 4) с каналом связи между терминалом и системой. Проблемы, связанные с управлением терминалом, возникают из-за неточностей в алгоритмах взаимодействия с каждым из этих объектов. Давайте рассмотрим перечисленные объекты в обратном порядке: • Большинство терминалов, .работающих с ASCII-кодом, подключены к системе посредством последовательной линии. Эта линия состоит из 24 проводов, соответствующих стандарту RS-232. С помощью последовательной линии удаленный терминал может быть подключен к модему. В этом случае компьютер также должен быть подключен к модему. Два модема поддерживают связь по телефонному каналу. В некоторых случаях конфигурация последовательной линии определяется на аппаратном уровне. Например, не в каждый кабель включаются все провода, которые предписаны стандартом RS-232, поэтому как для терминала, так и для системы (или модема) должно быть заключено соглашение относительно того, какой, провод и для какой цели применять. (В действительности как компьютерные системы, так и терминалы крайне неуступчивы в этом отношении. И для систем, и для терминалов было раз и навсегда определено, по какому проводу передавать данные, а по какому принимать. И если на обоих концах линии хотят для какой-либо цели использовать один и тот же провод, то необходимо вмешательство администратора системы, чтобы как-то примирить "враждующие стороны" путем перекрестного соединения проводов.) Под каналами связи следует подразумевать не только соединительные провода. Например, как для терминала, так и для системы (или модема) конфигурацию нужно определить так, чтобы были согласованы следующие параметры: количество битов данных, образующих символ (байт состоит из восьми битов, а для символов в кодировке ASCII требуется семь битов), контроль четности (простой метод контроля ошибок), стартовые/стоповые биты, "обрамляющие" символ, а также скорость передачи (в бодах). 664 Часть седьмая. Терминалы и принтеры
42.01 Обычно конфигурация выполняется заранее: если этого не сделать, система и терминал не смогут обмениваться данными. Тем не менее, команда stfy (41.02, 41.03) в любом случае позволяет динамически изменять эти параметры (по крайней мере, со стороны системы; терминал пользователя может иметь клавишу настройки, вызывающую встроенное меню). В этом случае нужно быть твердо уверенным в собственных действиях, иначе терминал и компьютер окажутся не в состоянии обмениваться данными. • В то время, когда появилась операционная система UNIX, не было стандартов, описывающих, как должен работать терминал. Экран размером в 24 строки и 80 колонок стал (в известной степени) стандартным, но специальные клавиши на клавиатурах терминалов генерировали различные символы, и каждый терминал должен был отвечать на различные управляющие последовательности (5щ для перемещения курсора по экрану, инверсного выделения текста, подчеркивания символов и т.д. Чтобы все это упорядочить, были разработаны базы данных termcap и terminfo (5.02). После того как характеристики терминалов были занесены в базу данных, у экранных программ (таких как редактор vi) появилась возможность доступа к информации, необходимой для очистки экрана, перемещения по экрану и т.д. Для чтения базы данных и использования содержащейся в ней информации с целью выдачи команд терминалу (в форме управляющих последовательностей) были разработаны программы наподобие [set (s.ii) и tput (5.12,41.10). Если постоянно используется терминал одного и того же типа, его можно сконфигурировать, вводя управляющие последовательности непосредственно с клавиатуры (41.09). При этом нет необходимости искать их в базе данных терминала. (Поиск управляющих последовательностей в базе данных выполняется в тех случаях, когда программа или сценарий работает с различными терминалами.) • Драйвер последовательного порта выполняет различные манипуляции с символами, полученными от терминала. Например, обычно он заменяет символ возврата каретки (ASCII-код \015), который генерирует клавиша [RETURN], символом перевода строки (ASCII-код \012). Крис Торек рассказывал о некоторых операциях преобразования в параграфе 41.02. В большинстве случаев, если только вы не программист и не системный администратор, не требуется знание всех этих возможностей. Необходимо лишь знать, что конфигурацию драйвера можно изменить и что stQ (41.03) — это программа, которая сообщает об установках драйвера (а также изменяет (S.09) их). Некоторые установки драйвера используются каждый день, и вам следует поместить их в свой файл .login или .profile (2.03). Например, откуда система знает, что сочетание клавиш [CTRL-c] вы применяете для прерывания программы, [CTRL-s] — для останова вывода, a [CTRL-z] — для того, чтобы приостановить выполнение программы? Все эти действия производятся на более низком уровне, чем уровень интерпретатора shell: сам интерпретатор никогда не видит этих символов, поскольку они интерпретируются и обрабатываются драйвером последовательного порта. Однако в некоторых ситуациях они не интерпретируются. Приходилось ли вам нажимать [CTRL-z] в режиме вставки редактора W? Вместо прерывания работы редактора нажатие этих клавиш вызывает ввод символа. Это происходит потому, что для редактора vi необходимо перевести в другой режим драйвер последовательного порта (41.02), чтобы он контролировал, какие символы отображать на экране, а какие интерпретировать как команды. Итак, в работе драйвера последовательного порта есть много "подводных камней". • И конечно, как мы уже подробно объясняли в процессе рассмотрения метасимволов (s.u), интерпретатор shell может перехватывать и обрабатывать различные символы перед тем, как передавать их другой программе. Цель этого длинного экскурса — показать, что для решения каких-либо проблем с терминалами необходимо иметь представление обо всех уровнях, на которых эти проблемы могут возникнуть. (Например, в параграфе 8.20 рассказывается об обработке обратной косой черты.) Сконфигурированы ли терминал и операционная система как следует? Не отсоединился ли кабель? Правильно ли установлен тип терминала, чтобы программы знали, какие команды выдавать этому терминалу? Не выдала ли прерванная программа незавершенные команды, что может ввести терминал в ненормальное состояние? Действительно ли проблема связана с терминалом или просто что-то работает не так, как вы ожидали? Вот приблизительный перечень вопросов, которые потребуется решить при возникновении проблем с терминалом. -ГОЛ Проблемы с терминалами 665
4Z.02 42.02 Как восстановить терминал или задание после зависания В данном параграфе рассмотрен целый рад методов возобновления заблокированных сеансов работы с терминалом или окном. Прекращение вывода Если терминал имеет клавишу [HOLD SCREEN] или [SCROLL LOCK], вы могли случайно нажать ее. Нажмите ее и посмотрите, не возобновится ли вывод. Если при нажатии одной из этих клавиш проблема не была устранена, нажмите ее еще раз, чтобы отменить ее действие, иначе вы еще сильнее заблокируете терминал! Вывод мог быть остановлен и в результате нажатия клавиш [CTRL-s]. Возобновить остановленный вывод позволит сочетание клавиш [CTRL-q]. (В отличие от клавиши [SCROLL LOCK], сочетание [CTRL-q] бессмысленно нажимать повторно, если одно нажатие оказалось безрезультатным.) Останов выпопнения задания Если вы получили приглашение интерпретатора shell (7.oi), а должны находиться в программе, которая, как вы считаете, выполняется, и если ваша система поддерживает управление заданиями, то, возможно, вы приостановили свое задание. Попробуйте ввести команду jobs, и если ваше задание остановлено, перезапустите его (i2.os). Программа ожидает ввода данных? Программа может ожидать, чтобы вы ответили на ее вопрос или направили текст на ее стандартный ввод. Если программа выполняет задачу, которую трудно отменить (например, удаляет файлы), не пытайтесь осуществить настоящий пункт, пока тщательно все не продумаете. Если в системе поддерживается механизм управления заданиями, можно перевести задание в фоновый режим, нажав клавиши [CTRL-z] и задав команду bg. Если задание ожидало ввода, на экране появится такое сообщение: [1] + Stopped (tty input) grep pat Задание можно снова перевести в интерактивный режим и ввести данные, если известно, что именно нужно ввести. В противном случае задание можно уничтожить, так как оно остановлено (см. ниже). В системах, которые не поддерживают управление заданиями, возможно, удастся ответить на все вопросы программы, нажав клавишу [RETURN] или другую клавишу, нажатия которой ожидает программа (например, у или и). Кроме того, можно попробовать нажать [CTRL-d] или любые другие клавиши, назначенные символу "конец ввода". Правда, это может привести к выходу из системы, если переменная ignoreeqf (З.05) не установлена. Потеря связи Если терминал подключен к компьютеру, модему или сети, убедитесь, что не нарушена проводная связь. При наличии у используемого модема функциональных индикаторов попробуйте нажать клавиши и посмотрите, не зажигается ли индикаторная лампочка. Световые сигналы можно трактовать следующим образом. Мигает индикаторная лампочка SD (Send Data — послать данные), значит, терминал посылает данные главному компьютеру, индикаторная лампочка RD (Receive Data — принять данные) — компьютер посылает данные терминалу. Отсутствие светового сигнала может свидетельствовать о том, что с терминалом что-то случилось. Если вы вошли в систему с помощью утилиты rlogin или telnet (из), то, возможно, в сети произошел разрыв соединения с удаленным компьютером или сеть стала работать очень 666 Часть седьмая. Терминалы и принтеры
42.02 медленно. Попытайтесь установить новое соединение с тем же удаленным компьютером. Если вы получили от него сообщение вида: Connection timed out (Разрыв соединения по тайм-ауту), у вас остается две возможности: 1. Подождать, пока не будет разблокировано первоначальное соединение. Соединение может вернуться в рабочее состояние, что позволит вам продолжить работу. 2. Завершить сеанс работы и войти в систему позднее. Преждевременное прекращение выполнения программы Многие пользователи для преждевременного завершения выполнения программы применяют сочетание клавиш [CTRL-c]. Можно установить другой символ прерывания, например [DELETE] или [RUBOUT]. Если и эти клавиши не действуют, попытайтесь нажать [CTRL-\j. В большинстве случаев эти действия вызовут завершение программы. Если же этого не произойдет, выполните следующее: 1. Войдите в систему с другого терминала или из другого окна. 2. Введите команду ps -x. В System V воспользуйтесь командой ps -u имя, где имя — это ваше пользовательское имя. Список программ, выполняемых в настоящий момент, будет выведен приблизительно в следующем виде: % ps - PIP 163 8532 22202 8963 24077 -X TTY i26 i26 i26 Pb Pb STAT I TW s R S TIME 0:41 2:17 12:50 0:00 0:05 COMMAND -csh (csh) vi ts.ms vi UNIXintro.ms ps -x -bin/csh (CSH) 3. Просмотрите этот список и найдите команду, работа которой вас не удовлетворяет. Запомните идентификатор процесса (PID) этой команды. 4. Введите команду kill PID (з».ю), где PID — это идентификатор, который вы запомнили на предыдущем этапе. Если приведенная команда не работает, попробуйте задать команду kill -1 pid, чтобы послать процессу сигнал HUP. Возможно, придется воспользоваться командой kill -9, но сначала попытайтесь ввести два первых варианта этой команды. 5. Если на терминале появилось приглашение UNIX (в виде % или S), то, очевидно, все пришло в норму. Однако, возможно, все еще необходимо предпринять определенные шаги, чтобы вывести терминал из неизвестного режима (42.04). Если приглашение интерпретатора shell не появляется, найдите интерпретатор, связанный с вашим терминалом (его номер указан в столбце TTY вывода команды ps), и примените к нему команду kill. Интерпретатор С shell запускается посредством команды csh, a интерпретатор Bourne shell — посредством команды sh. В большинстве случаев уничтожение интерпретатора shell приводит к уничтожению всех программ, которые были запущены с данного терминала. Непременно убедитесь, что вы применяете команду kill к интерпретатору, запущенному с терминала, за которым вы работали прежде, а не к интерпретатору, запущенному с того терминала, которым вы временно пользуетесь для выполнения данных команд. С временного терминала выполняется команда ps. Просмотрите приведенный выше пример. Найдите команду ps и терминал, с которого она была запущена (столбец TTY). В данном случае — это терминал pb. С помощью команды ps проверьте, действительно ли удален интерпретатор и тот ли это интерпретатор, который вы хотели удалить. Если интерпретатор все еще существует, примените более "сильную" команду kill -9 PID. 6. Выполните команду ps -x еще раз, чтобы убедиться, что уничтожены все процессы, запущенные с исходного терминала. (В некоторых случаях процессы могут сохраниться.) Удалите все оставшиеся процессы. 7- Теперь вы снова можете войти в систему с терминала, за которым работали раньше. Проблемы с терминалами 667
42.03 Команда ps, которая позволяет выводить список всех выполняемых пользователем программ, предоставляет также такую полезную информацию, как статус программ и время центрального процессора, затраченное на выполнение каждой программы. Обратите внимание на то, что команда ps выводит список всех программ, выполняемых пользователем, включая даже те, о которых он не знает (например, программы, автоматически запускаемые из других программ). — JP, ML, из книги UNIX for FORTRAN Programmers издательства O'Reilly & Associates Почему изменение переменной TERM иногда оказывается безрезультатным Использование команды tset для установки переменной среды TERMCAP (s.04) может вызвать проблемы у начинающих пользователей, которые не до конца понимают принцип действия этой команды. Например, пока не установлена переменная TERMCAP, программы по умолчанию выполняют поиск в базе данных /etc/termcap. Однако когда переменная TERMCAP содержит запись базы данных termcap, последующее изменение переменной TERM уже не окажет влияния на программы наподобие редактора vi. Если вы правильно присвоили значение переменной TERM, но редактор vi или другие программы, которые зависят от этой переменной, по-прежнему работают неверно, проверь- те (6.01), чтобы переменной TERMCAP не была присвоена фактическая запись базы данных termcap (4i.it). Эту установку можно отменить с помощью команды % unsetenv TERMCAP или $ TERMCAP= или $ unset TERMCAP (новые версии интерпретатора Bourne shell) - TOR 42.04 Восстановление исходного состояния терминала с нарушенными установками Злые гоблины (шумы в модеме, ошибки в программе, очень длинные строки выводимых данных, непечатаемые символы в файле, который вы выводите на экран с помощью команды cat, и т.д.) могут каким-то образом проникнуть в систему и вызвать неразбериху на экране или в окне. В чем это проявляется? На экране вдруг появляются группы непонятных мигающих знаков, набор символов превращается в иероглифы, слова становятся подчеркнутыми или выводятся инверсно, блокируется построчный вывод... Да чего только не бывает в таком случае! Рассмотрим, что можно сделать для устранения этих проблем. Некоторые из описанных нами действий нужно выполнить еще до того, как терминал может быть заблокирован. • Если вам удалось получить приглашение интерпретатора shell (% или $), в первую очередь, возможно, надлежит ввести одну из следующих команд: clear 22.18 % clear в системах с termcap tput 41.10 % tput dear в системах с terminfo В результате будет предпринята попытка очистить экран, а также устранить другие проблемы, например отменить режим инверсного отображения. • Если при выполнении команды clear экран не очистился полностью, а терминал имеет встроенное меню установок, попробуйте найти и выполнить функцию "очистить экран". (Если вы не знаете, как перейти в режим установок, воспользуйтесь документацией по терминалу или обратитесь за консультацией к эксперту. Запишите последовательность действий и повесьте эту инструкцию где-нибудь поблизости от терминала.) 668 Часть седьмая. Терминалы и принтеры 42.03
42.04 • Если на экране присутствует приглашение интерпретатора shell и вы находитесь в системе, использующей базу данных terminfo, попытайтесь выполнить следующие команды. Не вводите команду tput init, если команда tput reset не устранила проблему. % tput reset % tput init В системе, использующей базу данных termcap, отсутствуют команды, аналогичные приведенным выше. Их можно смоделировать, создав псевдоним (ю.о2) для команды tset (5.M) и выполнив его при входе в систему. (Почему бы не создать такой псевдоним сейчас, чтобы в следующий раз, когда вы окажетесь в затруднительном положении, вспомогательное средство уже было у вас под рукой?) Вот этот простой псевдоним: alias newterm 'set noglob; eval "tset -srQ \!*'; unset noglob' Команда tset обычно посылает терминалу команды сброса и инициализации. • Если каждый вводимый символ появляется в отдельной строке или же символы не отображаются вовсе, если нет никакой реакции на клавишу [RETURN] или при ее нажатии на экране появляется пара символов Лм, если не работают клавиши стирания, прерывания и удаления, если строки на экране располагаются лесенкой, то, возможно, что-то не в порядке с установками последовательного порта. Одна из следующих команд может вернуть терминал в рабочее состояние. Возможно, эти установки отличаются от тех, с которыми вы обычно работаете, но, по крайне мере, вам удастся выйти из системы и снова войти в нее: % reset % ICTRL-jl reset |CTRL-j|- % stty sane % ICTRL-JI stty sane |CTRL-j| (При наличии на терминале клавиши [LINEFEED] ее можно использовать вместо сочетания клавиш [CTRL-j].) Если система сообщает, что таких команд нет или установлен "неизвестный режим", следует создать псевдоним (io.o2), функцию интерпретатора shell ро.ю) или сценарий (44.02) для выполнения команды stty, которая подобна той, что приведена ниже. Точные параметры, которые необходимо использовать, зависят от настроек ващей системы: stty echo -nl-cbreak Назовите эту команду, например, sane.* Возможно, ввод этой команды следует начать и завершить нажатием клавиши [LINEFEED] или [CTRL-j]. Если эта команда не работает должным образом, надлежит принять такие меры: при следующем входе в систему, когда терминал работает нормально, введите команду stty everything или _stty -g. Это позволит вам точно определить, какие параметры использовать в команде sane. • В некоторых случаях система воспринимает каждый введенный символ как отдельную команду (при этом вы можете не видеть те символы, которые вводите): % reset г: Command not found : No previous regular expression : No current filename : No lines in the bufferq % * Это имя можно перевести так: "Привести в порядок" — Примеч. черев Пробммы с терминапами 669
42.94 (На экране это выгладит несколько иначе, потому что первая из двух букв е запускает редактор по имени е. Символы s, e и t воспринимаются редактором е как команды. Вам необходимо выйти из редактора е, введя команду q. Черт знает что!) Можно создать функцию shell или псевдоним, которые позволят выполнить одну из приведенных выше команд (reset, stty sane и т.д.) путем ввода одного-единственного символа. Добиться этого можно также за счет создания в своем каталоге bin (4.02) символической ссылки (им) или сценария интерпретатора shell. В качестве имени такой команды я выбрал символ ] (закрывающая квадратная скобка), а в своем каталоге bin я создал символическую ссылку с таким именем: % In -s /usr/ucb/reset ] (В вашей системе команда reset может иметь другое путевое имя.) Теперь, чтобы вывести терминал из неопределенного состояния, я ввожу символ ] в ответ на приглашение интерпретатора shell (ввод этого символа следует начать и завершить нажатием клавиши [LINEFEED] или [CTRL-j]). • Для сохранения установок терминала в файле лучше использовать команду stty -g (если таковая есть в системе). Затем, когда терминал снова войдет в неопределенное состояние, прочитайте эти установки из файла. Ниже показано, как это сделать. Сначала, когда терминал работает правильно, введите следующую команду: % stty -g >$HOME/.stty Затем создайте псевдоним, сценарий или функцию shell по имени sane, ] и т.п. (выше рассказывалось, как это делается), которые будут выполнять следующую команду: % stty 'cat $HOME/.stty' По этой команде для терминала должно быть восстановлено состояние, в котором он пребывал до первого выполнения команды stty -g. Если в системе отсутствует команда stty -g, можно создать команду с аналогичными функциями. Выполните команду stty everything или stty -а и просмотрите установленные значения: % stty everything speed 38400 baud, 0 rows, 0 columns parenb -parodd cs7 -cstopb -hupcl cread -clocal -crtsts -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc ixon -ixany -ixoff imaxbel isig iexten icanon -xcase echo echoe echok -echonl -noflsh -tostop echoctl -echoprt echoke opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel -tabs erase kill werase rprnt flush lnext susp intr quit stop eof Л? лц ли aR л0 av Az/*Y ЛС л\ "S/AQ AD В документации по команде stty прочтите материал, посвященный этим установкам (некоторые из них, например установки контроля четности, могут меняться от сеанса к сеансу). Поместите установки в созданный вами псевдоним с именем sane или ]: stty icanon echo erase ,Л?' kill ,ли' ... Обратите внимание, что при использовании различных терминалов каждый из них может иметь собственные установки. Создайте несколько версий команды sane. Требуемую версию можно устанавливать автоматически при входе в систему (2.12). Если проблему так и не удалось решить, попробуйте выполнить то, что предложено в параграфе 42.02. Найдите другой терминал, чтобы войти в систему под своим именем. Выполните команду ps, найдите процессы, запущенные с зависшего терминала или из заблокированного окна, и уничтожьте их. После этого выключите терминал или закройте окно и войдите в систему еще раз. (Если не удается уничтожить процессы перед повторным входом в систему, обязательно сделайте это сразу после входа в нее.) -JP 670 Часть седьмая. Терминалы и принтеры
42.05 42.05 Как восстановить размеры экрана Раньше размеры экрана терминала (количество строк и символов в строке) можно было устанавливать только в системах, использующих базы данных termcap и terminfo (5.02). Сегодня в большинстве версий UNIX такие экранно-ориентированные утилиты, как w и more, должны уметь работать с оконными системами. В этих системах пользователи могут свободно уменьшать и увеличивать размеры окон, не изменяя их определения в базе данных termcap или terminfo. В зависимости от используемой версии UNIX или выполняемой команды, можно столкнуться с проблемами, связанными с размером окна. Однако не все проблемы сопряжены с оконными системами. "Классические" проблемы, вызываемые, например, файлами со строками, длина которых превышает ширину экрана, существовали еще до появления оконных систем в UNIX. Этот параграф является памяткой по устранению проблем, связанных с изменением размеров экрана. • Если на терминале не все установлено как следует, возможно появление проблем при выводе на экран или при редактировании файла, в котором длина строк превышает ширину экрана. Для разбиения длинной строки на части используются два различных механизма. Проблема возникает в том случае, если эти механизмы не согласованы между собой (например, длина строки определяется по-разному): — Для терминала или окна может быть установлен режим автоматического переноса строк, которые пересекают правую границу экрана или окна: строка разбивается, и ее заключительная часть выводится в следующей строке, начиная с левой границы. — Экранно-ориентированные программы (такие как v/ и more) способны самостоятельно переносить длинные строки. Информацию о том, где разбивать строки, они берут из баз данных termcap и terminfo. Чтобы проследить поведение терминала, выведите на экран файл с длинными строками, например файл longlines (42.щ. Программы cat (25.02) и head (25.20) не используют базы данных termcap и terminfo: они выводят файл на экран таким, каким он есть. Поэтому, например, единая 200-символьная строка из файла longlines на экране с 80 колонками будет разбита на три части. При этом не должен потеряться ни один символ ни в одной части строки. Две строки на экране будут выглядеть следующим образом: % head -2 longlines 1 45678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890 1234567890123456789012345678901234567890 2 45678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890 1234567890123456789012345678901234567890 Если на экране отображается только одна часть каждой строки, значит, терминал не выполняет переноса строк. В таком случае можно войти в режим установок терминала и задать выполнение переноса. При этом необходимо позаботиться, чтобы в списке определений терминала баз данных termcap и terminfo было указано свойство автоматического переноса. Когда терминал выполняет автоматический перенос строк, но в базах данных termcap и terminfo ничего об этом не сказано, экранно-ориентированные прикладные программы также могут пытаться сделать это. Результат зависит от того, какая программа используется и какой у пользователя терминал. Часто вывод на экране выглядит так: за каждой частью длинной строки следует пустая строка. Например, файл longlines может иметь такой вид: 1 4 567890123456789012345678901234 5678901234567890123456789012 34567890123 123456789012345678901234567890123456789012345678901234 5678901234567890123 1234567890123456789012345678901234567890 Проблемы с терминалами 671
42.05 В данном случае перенос строк пытается выполнить как терминал, так и прикладная программа. Обычно прикладная программа не знает точно, сколько строк выведено на терминал, и посылает в два раза больше строк, чем может поместиться на экране. В общем, полная путаница! Попытайтесь использовать другое значение переменной среды TERM (5.Ю), соответствующее терминалу, для которого определено свойство автоматического переноса. Название терминала может иметь окончание am или aw, например: vtlOOaw. Можно немного поэкспериментировать и по-своему установить свойства терминала в формате базы данных termcap или tenninfo, которые берутся из стандартного системного файла. Дополнительную информацию по этому вопросу вы найдете в параграфе 41.11, а также в книге termcap & tenninfo издательства O'Reilly & Associates. • На экране пользователя может возникнуть настоящая неразбериха, когда полноэкранное приложение, например утилита постраничного вывода рг, отображает вывод какой-либо программы. Дело в том, что программа записывает некоторые сообщения в стандартный выходной поток ошибок. Эти дополнительные строки не учитываются приложением, но отображаются на экране. Если в приложении имеется команда перерисовки экрана (например, [CTRL-1]), можно повторно отобразить строки стандартного вывода, но уже без сообщений об ошибке. Можно также по каналу направить утилите постраничного вывода как стандартный поток вывода, так и стандартный поток ошибок (u.at). • Некоторые прикладные программы позволяют устанавливать размер экрана в файле конфигурации или в переменной среды. Например, в системе Berkeley программа mail о.зз) (в System V — mailx) имеет переменную screen, которой может быть присвоено значение в пользовательском файле .mailrc или в системном файле, таком как /usr/lib/Mail.rc. Редактор V/ имеет переменную window, которой может быть присвоено значение в файле .ехгс (4.ю). Если этим переменным невозможно присвоить правильные значения с учетом всех используемых терминалов, следует позволить прикладной программе самостоятельно устанавливать размер окна. • В вашей версии UNIX размер окна может храниться как часть установочных параметров драйвера устройства. Чтобы узнать размер экрана, введите следующую команду: % stty size 24 80 (Если эта команда не работает, попытайтесь выполнить команду stty -а и найдите в ее выводе параметры rows= и columns=.) Введенная команда сообщает, что по сведениям, имеющимся в ОС, размер экрана терминала равен 24 строкам и 80 символам. Кроме того, значение размера экрана может храниться в переменных среды COLUMNS и LINES: env 6.01 % env | egrep '(COLUMNS I LINES) = ' rep 27.05 COLOMNS=80 LINES=24 Если текущие установки не соответствуют фактическому размеру экрана (42М), их можно изменить (см. советы ниже). • Если системе не известен размер экрана и в ней имеется команда stty size, можно воспользоваться этой командой для изменения установок экрана. Например, если окно охватывает 43 строки и 80 колонок, введите такую команду: % stty rows 43 columns 80 Если прикладные программы используют переменные среды COLUMNS и LINES, просто присвойте этим переменным новые значения (б.оо. Примечание: После изменения размера экрана могут не работать те задания, которые были приостановлены или выполнялись в фоновом режиме. Это связано с тем, что они используют старые значения размера экрана. Такие задания следует завершить и запустить заново. Кроме того, им можно направить сигнал WINCH (см. ниже). 672 Часть седьмая. Терминалы и принтеры
42.06 • При выполнении программы xterm в системе X Window (ui) рекомендуется ввести команду resize, которая запрашивает у программы xterm размеры окна, а затем присваивает эти значения параметрам команды stty или переменным среды. Когда команда resize присвоит значения переменным среды, для переустановки размера экрана потребуется, чтобы новые значения стали доступными интерпретатору shell. В интерпретаторе С shell введите такие команды: сч:Л S.10 % set noglob; aval "resize' (Команду unset noglob (6.09) вводить не следует, так как ее уже выдает утилита resize.) В интерпретаторе Bourne shell необходима следующая команда: $ aval 'resize' Обе команды проще использовать, если создать псевдоним (ю.ог) или функцию интерпретатора shell (Ю.09), например, с именем га: alias rs 'set noglob; eval "resize"' rs () { eval Yusr/bin/Xll/resize~; ) • В UNIX-системах, где размеры окна хранятся как установки драйвера устройства (а не как переменные среды), можно сделать еще кое-что. Сначала введите команду stty, чтобы установить правильные размеры (см. выше). Затем пошлите сигнал WINCH (окно изменено) интерпретатору shell и (или) заданию, которое использует неправильные данные о размерах окна: % kill -WINCH $$ Послать сигнал интерпретатору shell % kill -WINCH %1 Послать сигнал заданию номер 1 • В некоторых оконных системах изменение размера окна приводит к переустановке соответствующих параметров. • Допустим, вы ввели команду telnet (из), чтобы зарегистрироваться на удаленной машине. На локальной машине никаких проблем с размерами окна не было. Так в чем же дело? Вероятно, команда telnet не передала удаленному компьютеру правильный размер окна. Если оба компьютера, локальный и удаленный, поддерживают утилиту rlogin (из), используйте ее вместо команды telnet. В противном случае укажите удаленной системе размер окна, пользуясь одним из наших советов. - JP 42,06 Файлы для проверки размеров экрана ' Сколько же строк и колонок на экране или в окне? Для некоторых программ этот ответ имеет колоссальное значение (42.05). Я создал каталог testingjiles и поместил в него несколько разработанных мною файлов для проверки и установки размеров экрана. шг~т Ширина экрана: файл 80cols Г (Щ) 1 Файл SOcols содержит строку из 80 цифр. Я использую этот файл для того, чтобы проверить, ^^ -^ охватывает ли окно в точности 80 колонок (рис. 42.1). 80cols Интервал в 80 символов Интервал в 20 символов Интервал в 10 символов ю 12345678 9012345 678 90123 4- — 5 €"18 90 Рис. 42.1. Файл 80cols Проблемы с терминалами 11 9-171 673
42.06 Шаблон повторяется через каждые десять символов, что облегчает подсчет колонок в окне. Некоторые UNIX-программы настроены на 80-колоночный экран. Поэтому, даже если на экране можно устанавливать более широкие окна, они вам просто не понадобятся. Если же вы работаете с более широкими окнами, для них тоже можно создать проверочные файлы. Ширина и высота экрана: файл screensize Файл screensize содержит 69 строк, пронумерованных от 69 до 1. Введите команду cat screensize и, когда содержимое файла отобразится на экране, подсчитайте число строк, заполнивших экран (рис. 42.2). 69 4567е9012Э45$7&9012Э<$«7890г2Э4567е9012Э4567в9<иЗЭ456?в9012315Г>*9012Э4567е90 6в 4567ав01234567ваОД234567е9012345в7а901234567в»оа2345679901234367а9012Э4567В90 ! о 9 45678901234567890123456789012345678901234567890123456789012345678901234567890 в 45678901234567890123456789012345678901234567890123456789012345678901234567890 7 45678901234567890123456789012345678901234567890123456789012345678901234567890 6 45678901234567890123456789012345678901234567890123456789012345678901234567890 5 45678901234567890123456789012345678901234567890123456789012345678901234567890 4 45678901234567890123456789012345678901234567890123456789012345678901234567890 3 45678901234567890123456789012345678901234567890123456789012345678901234567890 2 45678901234567890123456789012345678901234567890123456789012345678901234567890 1 45678901234567890123456789012345678901234567890123456789012345678901234567890 % ширина 80 символов Рис. 42.2. Проверка ширины и высоты окна с помощью файла screensize Ha рис. 42.2 верхняя строка имеет номер 9. Следовательно, окно содержит 10 строк (с учетом приглашения в последней строке). Файл screensize удобен также при использовании такой полноэкранной программы, как more (25.03), когда необходимо определить, правильное ли число строк и колонок выведено на экран. Когда программа more отображает первый полный экран, в самом верху должна находиться строка с номером 69. Последняя строка должна содержать приглашение приблизительно такого вида: —more—. При нажатии клавиши [ПРОБЕЛ] выводится следующая экранная страница. То же характерно и для редакторов типа vi. 674 Часть седьмая. Терминалы и принтеры
42.07 н longlines Установка ширины экрана, проверка переноса строк: файл longlines Файл longlines (рис. 42.3) напоминает файл screensize, но его 200-символьные строки являются слишком длинными для большинства экранов. Их можно использовать для двух целей: для установки особого размера окна и для проверки правильности переноса строк (42.05). На следующем рисунке показано, как выглядит перенос строк в редакторе Emacs, — в конце каждой переносимой строки отображается обратная косая черта. Обратная косая черта означает, что редактор Emacs выполняет перенос строки 1 45678901234567890123456789012345678901234567890123456789012345678901234667684 012Э456789012Э456789012Э45б7в9012Э456789012Э4567в9012Э4567890123456789012Э4$67$\ 9012345678901234567890 2 45678901234567890123456789012345678901234567890123456789012345678901234567894 01234567890123456789012345678901234567890123456789012345678901234567890123456784 9012345678901234567890 3 4567890123456789012345678901234567890123456789012345678901234567890123456789\ 01234567890123456789012345678901234567890123456789012345678901234567890123456784 9012345678901234567890 Emacs: longlines (Fundamental) Top Рис. 42.3. Отображение файла longlines редактором Emacs На 80-колоночном экране каждая строка файла longlines занимает две с половиной строки, если перенос строк выполнен правильно. (При использовании редактора Emacs не забывайте, что третья часть строки будет иметь на два символа больше, поскольку Emacs добавляет символ обратной косой черты в точке разрыва строки.) Как показано на рис. 42.3, никаких пустых строк на экране не должно быть. Если вы работаете с оконной системой типа X Window (i.jj), поищите в этой системе функцию изменения размера окна или функцию, предоставляющую информацию о параметрах окна. Например, менеджер окон twm отображает в небольшом окошке размеры окна, пока вы удерживаете нажатой кнопку мыши, изменяя размер окна. Не обязательно изменять размер окна: можно просто просмотреть текущее значение. Разнообразную информацию о размерах окна (в том числе и о размере окна в пикселях) предоставляет команда xwininfo. - JP 42.07 Сценарий termtest: выцача на терминал повторяющихся символов В этом сценарии используется команда yes (23.04), предназначенная для вывода на экран повторяющихся 79-символьных строк с максимально возможной скоростью. Сценарий полезен для обнаружения факта пропуска символов, шумов в коммутируемом канале и других погрешностей в работе терминала или соединения. При указании опции -Ь экран заполняется Проблемы с терминалами 22* 675
termtest 42M пробелами, за исключением 77-ой колонки, в которой выводится звездочка, позволяющая быстро находить лишние или выпавшие символы. Чтобы завершить выполнение сценария, воспользуйтесь клавишами прерывания ащ (например, [CTRL-c]). Две строки с командами yes, приведенные ниже, разбиты на две части, чтобы их удобнее (в) 1 было печатать. При вводе сценария разбивать строки не следует. #! /bin/sh # ИСПОЛЬЗУЙТЕ ПРОГРАММУ yes ДЛЯ НЕПРЕРЫВНОГО ВЫВОДА СТРОК СИМВОЛОВ НА ЭКРАН: case "$1" in еюс45.07 "") exec yes '()* + ,-./ 01234567 89:;<=>? 0ABCDEFG HIJKLMNO PQRSTUVW XYZ[] л_ 'abcdefg hijklran' ;; -b) exec yes ' *» . . *) echo "Usage: 'basename $0' [-b]" 1>&2; exit 1;; esac - JP 42.08 Сообщения об ошибках исчезают слишком быстро? Я сделал одно изменение в файле '.ехгс (4Щ и вызвал редактор vi, после чего заметил сообщение, выведенное в режиме негативного изображения. Затем экран был очищен и отобразился мой файл. Вероятно, промелькнувшее сообщение было сообщением об ошибке. Но редактор vi, как и некоторые другие программы, указал ошибки и сразу очистил экран, не дав возможности прочитать сообщение. Хорошо придумано, не правда ли? Ниже приведен ряд советов относительно того, как обойти подобную ситуацию: • Когда я использовал свой терминал с низкоскоростным каналом связи (1200 бод и ниже), мне обычно удавалось прочитать сообщение об ошибке еще до очистки экрана. Сейчас, когда скорость передачи данных достигает 28800 бод и выше, приходится имитировать работу с низкой скоростью. Выйдите из системы, установите для терминала или для коммуникационного модуля низкую скорость передачи данных и опять войдите в систему. Ну вот, совсем другое дело! • Давным-давно, когда в качестве терминалов использовались настоящие телетайпы (<п.ю). находить ошибки было просто: тогда еще не было экранов, которые очищались, а сообщения об ошибках печатались на бумаге. Поэтому, если ваш терминал оснащен принтером, включите его. (В DOS попробуйте нажать [CTRL-PrtScr].) При повторном запуске программы сообщение об ошибке будет выведено на печать. • Если окно или коммуникационная программа имеют функцию "перехватывать данные в файл", активизируйте ее. Теперь сделайте так, чтобы ошибка повторилась. Отмените функцию перехвата (это важно!). Затем прочитайте то, что записано в файл. Возможно, придется использовать команду cat -v (2S.07) или программу vmore (2s.os), чтобы не позволить символам очистки экрана, которые записаны в файл, помешать вам читать файл. • Если в вашем распоряжении нет функции для записи содержимого экрана в файл, но имеется программа script (si.os), выполните те же действия, что и в предыдущем пункте, пользуясь указанной программой. • Если окно снабжено полосой прокрутки или командой перемещения на одну экранную страницу вверх (page up), попробуйте воспользоваться одним из этих средств. Некоторые команды очистки экрана не очищают буфер прокрутки. • Воспользуйтесь командой ш оз.ю), чтобы в процессе просмотра информации записать в файл стандартный поток вывода и стандартный поток ошибок: |'& 13.05 % vi foo |£ tee saved_stuff csh 2>&l 8.13 % vi foo 2>fil | tee ssved stuff sh 676 Часть седьмая. Терминами и принтеры
42.08 Затем просмотрите сохраненный'файл посредстюм программы постраничного вывода. Вы можете предварительно отфильтровать его с помощью команд cat -у (25.07) и (или) fold (43.щ. (Именно, для заданий этого типа разработан сценарий vmore (25.05):) Еще раз запустите программу и повторите ее до того момента, когда возникает ошибка! Попытайтесь нажать клавиши [CTRL-s] или [HOLD SCREEN] после вывода сообщения об ошибке, но перед очисткой экрана. Возможно, это будет трудно сделать при работе в сети, поскольку в этом случае проходит некоторое время с момента нажатия клавиши до момента прекращения вывода. Временно используйте (б.ю) ту запись из базы данных termcap или terminfo, которая не содержит свойства очистки экрана. Хорошими установками для переменной TERM являются dumb и unknown. Предыдущий прием позволит прочесть сообщение на экране, но не позволит вам продолжить работу в редакторе 'vi. Если у вас часто возникают проблемы при работе с полноэкранными программами (например, с редактором vi) или вам регулярно приходится создавать файлы установок, такие как .ехгс, следует найти в базе данных termcap или terminfo такое определение свойств терминала, которое отличается от используемого вами определения лишь тем, что запрещает выполнять очистку экрана. (Вероятно, вы захотите проверить такие свойства базы данных termcap, как с/=, is=, if=, rs=, rf=, r2= и, может быть, ti=. При использовании базы данных terminfo поищите установки clear=, iprog=, is2=, if=, rs2=, rf= и в некоторых случаях smcup=.) Если вы не знаете, как это сделать, прочтите книгу termcap & terminfo издательства O'Reilly & Associates или попытайтесь поймать какого-нибудь гуру UNIX где-нибудь в коридоре университета.* Подсказка: все компьютерные ГУРУ любят пиццу. : -) Проблемы с терминалами 677
43 Печать 43.01 Немного истории Эта глава посвящена нетривиальным вопросам — вопросам печати. Чтобы понять сложность предмета, подумаем немного о том, что, собственно, мы собираемся печатать. Раньше, в старые добрые времена, использовались строчные принтеры и их "родственники" — принтеры типа "ромашка", матричные и прочие аппараты, выдававшие текст как на пишущей машинке. Напечатать простой текст было легко — не требовалось никакой дополнительной обработки, а единственное условие печати документа состояло в наличии программного обеспечения для вывода файла на принтер. При желании можно было добавить титульный лист и отформатировать текст, однако соответствующие средства были довольно примитивными. Одна из сложностей, возникающих при печати, связана с системой буферизации (spooling system), которая наряду с передачей данных на принтер должна выполнять ряд других задач. Большинство принтеров были (и остаются) устройствами совместного доступа. Это означает, что несколько пользователей могут одновременно посылать задания на принтер. Кроме того, может существовать несколько принтеров, на которых возможна печать файла, причем вы выбираете, какой принтер использовать. Система буферизации должна самостоятельно решать все эти задачи: получать данные от пользователей, выяснять, свободен ли тот или иной принтер, посылать файл на печать (если принтер свободен) или сохранять его (если принтер занят). Историческое замечание: почему система буферизации получила название spooling system? Дэйв Бирнбаум (Dave Bimbaum), главный научный сотрудник компании Xerox, отмечал: "Программа SPOOL (Simultaneous Printing Off and On Line) была написана для первых мэйнфреймов IBM (семейство компьютеров с трехзначными номерами, например 709) и впоследствии распространилась на первые машины семейства 1401. Данные, подлежащие печати, направлялись в программу SPOOL, которая либо сразу выводила их на печать, либо ставила в очередь (сохраняя на ленте) для печати в другое время. Существовала также версия программы второго поколения, в которой машина 1401 служила контроллером принтера для машины 7094. Две машины обменивались данными через ленточный накопитель, которым могли управлять обе машины." [Относительно значения аббревиатуры SPOOL есть и другие мнения, но расшифровка Дэйва лучшая из тех, которые я слышал. — JP] Несколько первых параграфов данной главы (43.02, 43.03, 43.04 и 43.05) посвящены базовой системе буферизации UNIX и способам работы с ней со стороны пользователя. (Мы не рассматриваем административные аспекты буферизации, так как это намного более сложный вопрос, не вполне соответствующий тематике данной книги.) В параграфе 43.06 описан способ печати с помощью терминала, имеющего собственный принтер. В следующих нескольких параграфах рассказывается, как форматировать тексты для печати. Речь идет не о том вычурном форматировании, которое в первую очередь приходит на ум современным пользователям, а о таких простых вещах, как разбивка на страницы, создание 678 Часть седьмая. Терминалы и принтеры
43.02 полей и т.п. в текстовых файлах, посылаемых на принтер. Эти элементарные приемы форматирования описаны в параграфах 43.07—43.10. Почему программы буферизации печати называются 1р или 1рг> Потому что обычно они принимают текст для вывода на строчный принтер (line printer) — быстрый принтер с широкой головкой, печатающей сразу всю строку. Такие принтеры все еще характерны для систем обработки данных, они буквально "выстреливают" страницы! В середине 70-х гг. пользователи UNIX живо заинтересовались типографским набором. Некоторые наборные устройства (самое популярное — фотонаборная машина С/А/Т) подключались прямо к компьютеру. Были разработаны программы (например, troff и ТЕХ), форматирующие текст для фотонаборных устройств, и пользователи компьютеров стали считать, что поскольку они могут создать красивый текст, то у них есть художественный вкус. Однако в большинстве случаев это было далеко не так. Если проследить историю компьютерного типографского набора и лазерных принтеров, то можно вспомнить массу чрезвычайно неуклюжих документов, дизайн которых считался отличным. Но это другая история. Такие инструменты, как troff, nroff (аналог troff, создающий выводные данные для стандартного терминала) и ТЕХ, все еще с нами и не утратили своей значимости (см. параграфы 43.12—43.21). Лазерные принтеры получили широкое распространение в середине 80-х. В то время у каждого пользователя появилась возможность получать напечатанные документы хорошего качества, правда не такого высокого, которое обеспечивала типография. Вместе с лазерными принтерами появился стандартный язык PostScript, предназначенный для управления принтером. Такие инструменты, как troff w ТЕХ, служили для создания выходного файла в формате PostScript, который можно распечатать на любом принтере, понимающем язык PostScript. Это было большим достижением: покупка нового принтера не влекла за собой смену программного обеспечения. Можно было передать PostScript-файл в другой конец страны и быть уверенным, что получатель сможет правильно напечатать его.* Однако возникла другая проблема. PostScript является сложным языком. Те операции, которые с легкостью производились с простым текстовым файлом, теперь стали довольно трудными. Оказалось, что невозможно набрать букву и послать ее на принтер типа "ромашка": сначала нужно преобразовать ее в формат PostScript. Раньше без проблем осуществлялись такие операции, как печать диапазона страниц из средины файла, поиск в файле с помощью программы grep, просмотр файла на экране. Сейчас все по-другому. Настоящая глава завершается описанием нескольких утилит для работы с PostScript-файлами. К сожалению, они имеют недостатки. Я испытал многие из них, и хотя среди таких утилит есть и хорошие, гораздо больше плохих, которые иногда работают, а иногда — нет. Хорошие утилиты рассмотрены в параграфах 43.22, 43.23 и 43.24. Наконец, параграф 43.25 посвящен пакету netpbm, который полезен тем, кто работает с графическими файлами. Пакет позволяет выполнять преобразование файлов в различные графические форматы. - ML 43.02 Основные команды вывода на печать Персональные компьютеры часто работают с выделенными принтерами. Выделенный принтер подключен только к вашей машине, и только вы вправе использовать его. За раз ему можно послать только одно задание, после чего нужно ждать, когда завершится печать и можно будет возобновить работу. В UNIX практикуется буферизация печати, благодаря которой многие пользователи могут совместно использовать один и тот же принтер. Пользователь может запросить печать в * Документы были переносимыми и раньше, в эпоху строчных принтеров. Переносимость исчезла с появлением компьютерного типографского набора и возродилась, когда PostScript стал доминирующим языком описания страниц. |Даже если принтер поддерживает режим PostScript, печать невозможна, если в системе нет шрифтов, которые использованы в PostScript-файле, и если описание этих шрифтов не включено в данный файл. — JP| Печать 679
43.02 любое время, даже если принтер занят. Запросы ставятся в очередь, а затем последовательно обрабатываются по мере того, как освобождается принтер. UNIX позволяет подключать к одной системе несколько принтеров. Один из принтеров устанавливается как выбираемый по умолчанию, и задания на печать автоматически посылаются на него. Команды печати в System V В System V команда 1р предназначена для включения задания в очередь на печать. (Команды систем типа Berkeley описаны ниже.) После вызова команда 1р помещает файл в буфер печати и возвращает идентификатор запроса на печать. Впоследствии этот идентификатор можно использовать для прерывания задания. $ lp notes request-id is lp-2354 (1 file) Команда Ipstat служит для проверки состояния очереди на печать. Она позволяет узнать, находится ли ваше задание в очереди: $ ipstat 1р-2354 14519 fred on lp Сообщение on lp указывает, что задание печатается. Если задание отсутствует в списке, значит, оно выполнено. Если же задание значится в списке, но нет сообщения on lp, можно сделать вывод, что задание еще находится в очереди. Опция -и позволяет определить состояние всех заданий в очереди, а команда cancel — прекратить выполнение задания. $ ipstat -u 1р-2354 14519 fred on lp lp-2355 21321 alice lp-2356 9065 John $ cancel lp-2356 lp-2356: cancelled При помощи команды Ipstat можно узнать, какие принтеры подключены к системе и каковы их имена. Если принтеров несколько, вместе с командой lp используется опция -d, позволяющая указать другой принтер. Например, если лазерный принтер сконфигурирован как laserp, то можно ввести: $ lp -dlaearp myfila Команды печати в Berkeley-системах В BSD UNIX для постановки задания в очередь на печать используется команда Ipr, которая помещает файл в буфер печати. $ ipr notes В отличие от команды lp в System V, команда Ipr не выводит идентификатор запроса. Если необходимо уничтожить задание, сначала вводится команда Ipq, которая предоставляет сведения о состоянии заданий, выводимых на печать. $ Ipq lp is ready and printing Rank Owner Job Files Total Size active fred 876 notes 7122 bytes 1st alice 877 standard input 28372 bytes 2nd John 878 afile bfile ... ■ 985733 bytes Слово active в столбце Rank является обозначением задания, которое в данный момент печатается. Если задание отсутствует в списке, значит, его печать завершена. Если же задание неактивно, то оно пока находится в очереди. Команда Iprm служит для удаления задания. (Сначала запустите команду Ipq, чтобы узнать номер задания.) 680 Часть седьмая. Терминалы и принтеры
43.03 $ lprm 877 dfA877сервер dequeued сfA877сервер dequeued Посредством команды Ipc status (4з.оз) можно определить, какие принтеры подключены к системе и каковы их имена. Если принтеров несколько, вместе с командой lpr используется опция -Р, позволяющая задать другой принтер в качестве стандартного. Например, если лазерный принтер сконфигурирован как laserp, то можно ввести: $ lpr -Plaserp myfile Опцию -Р применяют также с командами Ipq и lprm. Если вы часто используете определенный принтер, поместите его имя в переменную среды PRINTER (43.04). - DD, TOR, JP 43.03 Управление принтером с помощью команды Ipc Команда //>с(8) в Berkeley UNIX предназначена в основном для суперпользователя, но несколькими ее подкомандами могут пользоваться все. Эти подкоманды описаны в данном параграфе. В вашей последовательности поиска (S.07), возможно, нет каталога /etc или /usr/etc. Тогда вам придется запускать команду Ipc, указав полное путевое имя. Команды Ipc можно вводить по приглашению 1рс>. Завершив работу, следует ввести exit (или нажать [CTRL-d]). % /etc/Ipc lpc> help status status show status of daemon and queue lpc> ... lpc> exit % Команды Ipc вводятся и по приглашению интерпретатора shell: % /etc/lpc status imagen imagen: queuing is enabled printing is enabled no entries no daemon present % Демон (1,14) принтера наблюдает за очередью заданий, созданных пользователями посредством команды Igr (43.02). Если постановка в очередь запрещена (обычно это делает системный администратор), команда lpr не выполняется. Команда Ipc управляет только теми принтерами, которые подключены к локальному серверу. Принтерами, подключенными к другим серверам, эта команда управлять не будет, хотя на локальном компьютере можно проверить очередь заданий (если таковая имеется), ожидающих готовности удаленного принтера. Следующие команды доступны для всех пользователей: restart [принтер] Эта команда пытается запустить новый демон принтера. Используйте ее, когда по какой-то причине завершилась работа демона в то время, когда в очереди еще есть задания (команда Ipq или Ipc status сообщит об этом). Пользуйтесь этой командой, когда администратор отсутствует, а принтер не работает. Чтобы запустить все принтеры, можно вместо имени принтера указать all. В отличие от команды lpr, перед именем принтера не требуется ставить опцию -Р. Например, чтобы указать принтер foobar в команде lpr,, необходимо набрать lpr -Pf oobar. В команде Ipc следует использовать команду restart foobar. Печать 681
43.04 status [принтер] Благодаря этой команде можно определить состояние демонов и очередей на локальном компьютере (см. предыдущий пример). Чтобы узнать о состоянии всех принтеров, вместо имени принтера следует указать all. help [команда] Данная команда по умолчанию выдает список команд 1рс, в том числе предназначенных только для суперпользователя. Укажите имя команды, и команда help объяснит, каково ее назначение. exit Выход из 1рс. - JP 43.04 Использование различных принтеров Каждый принтер в системе должен иметь имя. По умолчанию команды, посылающие файл на принтер, предполагают, что принтер имеет имя 1р. Данную аббревиатуру можно расшифровать как "line printer" (строчный) или как "laser printer" (лазерный). Если вы работаете на однопользовательской рабочей станции, непосредственно к которой подключен принтер, назовите его 1р. Во многих системах вариантов конфигурации гораздо больше. Приведем примеры. Во-первых, в распоряжении сотрудников организации может быть несколько принтеров на выбор. Во-вторых, сами принтеры могут различаться по характеристикам (поддержка режима PostScript и т.д.). Выбор принтера осуществляется двумя способами: • В BSD UNIX команды печати воспринимают опцию -Рпринтер. К таковым относятся команда Igr (43.02), различные сценарии форматирования документов для типографского набора и т.д. Например, команда lpr -Pps file.ps посылает файл file.ps на принтер с именем ps. (Между прочим, ps является вторым по популярности именем принтера после 1р.) • В BSD UNIX команды распознают переменную среды PRINTER (t.oi). Если эта переменная определена, то команда читает ее значение и в соответствии с ним выбирает принтер. Так, команда % setenv PRINTER ps или $ PRINTER=ps ; export PRINTER обеспечивает отправку документов на принтер с именем ps. • В System V команды печати (например, 1р) для выбора принтера пользуются опцией -d. Например, команда lp -d pr file.ps посылает файл file.ps на принтер с именем рг и эквивалентна команде lpr из предыдущего примера. • В System V команды печати ищут переменную среды LPDEST, а не PRINTER. Так, команда % setenv LPDEST ps или $ LPDEST=ps ; export LPDEST обеспечивает отправку документов на принтер с именем ps. Заметим, что в System V Release 4 употребляются команды печати, применяемые как в System Y, так и в BSD (lp и lpr). Это может вызвать путаницу, особенно если вы используете сценарий для обработки troff- или ТЕХ-документов, и этот сценарий автоматически посылает документ на принтер. Если вы не знаете, как работает сценарий, то и не осведомлены, какую 682 Часть седьмая. Терминалы и принтеры
43.06 переменную следует устанавливать. Я бы посоветовал устанавливать обе переменные — PRINTER и LPDEST. Между прочим, можно поступить аналогичным образом при наличии единственного принтера, который имеет имя, отличное от 1р. Нужно только задать соответствующее имя в качестве значений переменных PRINTER и LPDEST. - ML 43.05 Использование символических ссылок при печати В процессе печати файл копируется в каталог буферизации печати. В случае необходимости напечатать файл большого объема это может быть сопряжено с проблемами следующего характера: операция копирования займет много времени или же каталог буферизации переполнится при копировании. В системах BSD UNIX перечисленные проблемы позволяет решить команда Ipr. Опция s обеспечивает создание символической ссылки (it.04) на файл из каталога буферизации печати. Вот пример такой команды: % Ipr -s directions Вместо того чтобы копировать файл directions, команда Ipr создает символическую ссылку на него. Создание ссылки гораздо быстрее по сравнению с копированием и вряд ли приведет к ошибке типа filesystem full (файловая система переполнена). Использование символических ссылок имеет один весьма существенный побочный эффект. Поскольку файл не скрыт в отдельном каталоге буферизации печати, вы можете удалить или изменить его после ввода команды Ipr и до завершения печати, что чревато неприятными последствиями. Поэтому постарайтесь так не делать. Конечно, подобное предупреждение касается только файлов, которые посылаются на принтер. Например, при форматировании lrqff-файла (43.13> для PostScript-принтера можно продолжать модификацию этого файла. Если соответствующий PostScript-файл буферизуется с помощью команды Ipr -5, следует соблюдать осторожность. Однако проблемы, скорее всего, не возникнет. Большинство пользователей применяют для запуска программы troff специальный сценарий, который предотвращает просмотр или изменение PostScript-файла. - ML 43.06 Печать на терминальном принтере Имеет ли ваш терминал дополнительный порт на задней панели для подключения принтера? Возможно, к этому порту можно подключить принтер с последовательным интерфейсом. Тогда вы сможете создать небольшой сценарий с именем myprinl, который действует следующим образом: % myprint файл % программа | myprint Сценарий myprinl может быть таким простым, как этот: echo "\033[5i\c" cat $* echo "\033[4i\c" или этот: escape='echo -n e I tr e '\033'" echo -n "$escape[5i" cat $* echo -n "$escape[4i" Это зависит от того, какая версия команды echo (46.Ю) используется в вашей UNIX-системе. Для вашего терминала может понадобиться другая Escape-последовательность, а эта предназначена для терминалов, совместимых с VT100 (см. параграфы 41.10 и 5.02). Некоторые Печать 683
43.07 терминалы требуют постановки символа новой строки после Escape-последовательностей. Если ваш терминал не работает с этим сценарием, попробуйте ввести команду echo без \с или без опции -п. Ваш принтер "глотает" символы? Вероятно, есть проблемы с управлением потоком данных. Попробуйте задать более низкую скорость обмена данными с терминалом. - JP 43.07 Быстрое форматирование перед печатью Программа печати (43.02) выводит на принтер файл в том виде, в каком он послан ей. Если направить этой программе непрерывный поток текста (и принтер настроен на печать текстовых файлов, а не PostScript-файлов), то результат будет следующим: никакого разделения на страницы, никаких отступов и других элементов форматирования. В данном случае уместно применять утилиту рг. Это простая утилита форматирования, разбивающая входной поток на "страницы", вмещающие 66 строк (стандарт США). Программа автоматически добавляет заголовок, включающий дату, время, имя файла и номер страницы. Кроме того, добавляется нижний колонтитул, выступающий гарантом того, что текст не выйдет за пределы страницы. Это именно то, что нужно, если на принтер посылается исходный текст программы или другой непрерывный текстовый поток. В подобном случае команда рг удобна также для вывода текста на экран. Наряду со стандартными возможностями она обладает многочисленными полезными опциями. ~f Разбивка на страницы с помощью символа перевода страницы (AL), а не последовательности пустых строк. -Ьстрока Замена стандартного заголовка строкой (см. параграф 35.17). -in Установка длины страницы равной л строк (по умолчанию — 66). ~т Объединение файлов и печать каждого из них в отдельном столбце (не может использоваться вместе с опциями +число и -а). Для размещения в столбце строки урезаются. См. параграф 35.17. Команда paste (35.is) выполняет аналогичные операции. -sc Разделение столбцов символом с (по умолчанию — символ табуляции). -t Пропуск заголовков страниц и завершающих пустых строк. -ычисло Установка длины строки в столбце равной числу (по умолчанию — 72). + число Вывод на печать, начиная со страницы с указанным номером (по умолчанию — 1). -л Формирование вывода в л столбцов (по умолчанию — 1). См. параграф 35.17. Некоторые опции применяются только в System V. -а Формат с несколькими столбцами. Элементы текста выводятся во всех столбцах одной строки, затем — другой и т.д. -d Печать через два интервала. -есл Установка позиций табуляции кратными л (по умолчанию — 8) и использование символа с в качестве разделителя полей (по. умолчанию — символ табуляции). _F Перенос входных строк (предотвращение урезания при наличии опции -а или -и). -i.cn Замена л пробельных символов символом с (по умолчанию с — символ табуляции, а л=8). -пел Нумерация строк числами длиной л цифр с последующим разделительным символом с (по умолчанию с — символ табуляции, а л=5). -°" Сдвиг каждой строки на л пробелов (по умолчанию — 0). "Р Пауза перед печатью каждой страницы. -г Подавление сообщений о ненайденных файлах. Часть седьмая. Терминалы и принтеры
43.09 Обобщим приведенные сведения в нескольких примерах. • Печать содержимого файлов в трех столбцах; при этом опускаются заголовки и завершающие пустые строки: pr -m -t list.l list.2 list.3 • Упорядочение списка штатов по алфавиту с нумерацией строк. Вывод в пять столбцов. Команда для System V: sort states_50 | pr -n -5 В BSD-системах, не поддерживающих опцию -«, можно использовать команду cat -n (25.21) для генерации номеров строк: sort states_50 I cat -n | pr -5 Для получения в BSD такого же вывода, как в System V, необходимо дополнительно установить длину страницы: . sort states_50 | cat -n | pr -t -5 -1 10 | pr -h states_50 — TOR, DG, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates 43:08 Установка полей с помощью команд рг и fold Версия команды рг (43.07) в System V имеет опцию -F, предназначенную для переноса строк, которые являются слишком длинными для выходной страницы. Благодаря этой опции концы длинных строк не удаляются. Когда печати подлежит большой объем разнородных данных, когда текст содержит длинные строки, когда ваша команда рг не поддерживает опцию -F, воспользуйтесь командой fold. Эта команда произвольным образом разбивает слишком длинные строки (по умолчанию — на 80-й позиции). Пользуйтесь опцией -ширина, где параметр ширина задает иную позицию, в которой выполняется перенос строки. Я создал псевдоним (io.o2) и функцию shell (гв.оя) с именем prF, которые производят все указанные операции. С их помощью форматируется файл, в заголовок которого помещается его имя (если данные направлены команде рг по каналу, она не знает имени файла). Можно также добавить команду | 1рг в конце определения псевдонима: alias prF 'fold \!A | pr -h "\!""' Удобно нумеровать перенесенные строки. В версиях команды рг, которые не имеют опции csh init, -F; обычно отсутствует и опция -п. Номера строк добавляются в псевдоним посредством shjnit команды cat -n (is.it). За перенесенной частью строки номер не закрепляется: О W alias prnF 'cat -n \!Л I fold | pr -h "\!Л"' ^^^^ Разбивку строк после слова вблизи правой границы, а не в конкретной позиции обеспечивает eshjnit,' команда fnU (35.02). shjnit _ jp 43.09 Отступы при печати При печати обычного текстового файла (не содержащего операторов языка PostScript или другого языка описания страниц) можно образовать отступы на странице путем добавления нескольких пробелов в начале каждой строки. Используйте редактор sed (34.24) для добавления в начале строки одного-двух символов табуляции (если ваш принтер распознает их) или пробелов (в противном случае). Приводим простую команду, которая добавляет четыре пробела в начале каждой строки файла logdata и выводит его на печать посредством команды Ipr: % sed 's/A/ /' logdata | lpr Печать 685
43.10 Если печатаемый файл содержит символы табуляции, может возникнуть проблема, связанная с тем, что дополнительные пробелы в начале строки нарушают позиции табуляции. В подобных случаях замените символы табуляции пробелами с помощью команды expand (41.04): % expand logdata | sed 's/A/ /' I lpr Благодаря команде pr (43.07) вы получите красиво сформатированные распечатки. Ее опции позволяют задать отступы для форматируемого текста. Если ваша команда рг не имеет опции -о (offset — сдвиг), пропустите ее вывод через фильтр sed: % pr logdata | sed 's/A/ /' I lpr В некоторых случаях результат выполнения данной команды не удовлетворит вас по следующим причинам. Если файл содержит символы табуляции, команда рг может не заменить их пробелами, хотя ее некоторые версии имеют опцию -е, используемую в этих целях. Кроме того, отступы создаются и для заголовков, которые из-за этого могут выступить за пределы правой границы листа. Приводим другую команду, решающую указанную проблему. Она заменяет символы табуляции и образует отступы в теле текста, не затрагивая заголовки: % expand logdata | sed ' s/V /' | pr -h logdata I lpr Опция -h logdata обеспечивает вставку имени файла в начале каждой страницы (команда рг не может получить имя файла другим способом). Если хотите, опустите эту опцию. Сценарий offset (35.07> можно вызывать с опциями, позволяющими устанавливать отступы. - JP 43.10 Вывод имени файла в начале распечатки Команда рг (43.о7) выводит файлы с симпатичным заголовком в верхней части. Она способна также добавлять несколько пустых строк для заполнения страницы и, если содержимое файла охватывает несколько страниц, снабжать каждую из них заголовком. В настоящем параграфе описаны средства, альтернативные команде рг. Они позволяют печатать единственный заголовок, а после него — весь файл без дополнительных пустых строк или разделителей страниц. 1. Если вывод команды more (2.1.0З) (или pg) переадресован в файл, после вывода каждой экранной страницы пауза не возникает. В начале вывода каждого файла сообщается имя файла, а далее следует содержимое. Вместо переадресации вывода в файл можно передать его по каналу другой программе, например программе буферизации: cat 25.02 \ more file* > package % cat package filel . . . содержимое filel file2 . . . содержимое file2 Такие же .чаголовки можно получить другим способом, а именно: воспользоваться особенностью команды head (2S..20). Если она обрабатывает несколько файлов, то добавляет заголовки в начале каждого из них. Чтобы указать команде head выводить весь файл (а не только заголовок), установите для счетчика строк значение, превышающее количество строк в любом из файлов. Это можно сделать с помощью такой команды, как head -10000. 2. В интерпретаторе Bourne shell циклы for с переадресацией вывода (45.22) позволяют объединять несколько команд и перенаправлять их вывод. Приводим цикл, который запускает команду Is -l для каждого файла. Команда си± (35.14) вырезает символы прав 686 Часть седьмая. Терминалы и принтеры
43.11 доступа к файлам (колонки 1 — 11, включая пробел), значение времени последней модификации и имя (колонки от 42-й до конца). (Вместо этого можно было бы направить вывод команды Is -I в программу типа sed, чтобы создать более красивый заголовок, а заодно избавиться от команд echo.) Вывод перенаправляется в файл с именем printme. Как указывалось, канал к вашей программе вывода на печать также будет работать. % for f in file* > do > echo =—i— > Is -1 $f | cut -cl-11,42- > echo = r- ii =x > cat $f > done > printme > cat printme Oct 28 07:28 filel ..содержимое filel -r~r~r- Nov 3 09:35 file2 ...содержимое fi1e2 Если вы часто используете два последних приема, поместите соответствуюший код в псевдоним, функцию или сценарий интерпретатора shell oo.oi). - JP 43.11 Крупные буквы: команда banner ##### # # # # ##### # # # # ###### # ##### # # ##### ## # # # # ###### # # # # ##### # # # # # # # # ##### ##### # # # # # # # # # ###### # # # # # # # f » # # # #### f #### » # # #### ### ### ### Я создал эту надпись с помощью команды banner (в System V), набрав: % banner "read this." Каждый аргумент (до 10 символов) выводится в стандартный выходной поток (H.oi) в виде букв, которые показаны выше. Я заключил два слова в кавычки, чтобы они выводились в одной строке. Если ввести эти слова как отдельные аргументы, то команда banner напечатает их одно под другим. Berkeley-версия команды Ааииегтакже позволяет печатать крупные буквы. Однако они намного крупнее и выводятся вдоль страницы с расчетом на принтер с непрерывной подачей бумаги. Такие надписи хороши для плакатов. По умолчанию буквы имеют высоту 132 символа, что достаточно для принтера с широкой кареткой или стандартного принтера в режиме сжатой печати. Посредством опции -w создают буквы высотой 80 символов. Чтобы достигнуть другой высоты, необходимо добавить число. Например, опция -100 задает высоту букв, равную 100 символам. Программа banner обычно находится в каталоге /usr/games. Хотя сообщение можно ввести и в командной строке, лучше вводить его по приглашению Message: (недокументированному), поскольку тогда не нужно защищать спецсимволы. Например: Печать 687
43.12 % /usr/gamas/banner -w | lpr -Ptractor Message: * * Happy Birthday, Alice!! * * Если у вас нет принтера с непрерывной подачей бумаги, для получения плаката прибегните к комбинированию отдельных страниц с помощью цикла foreach интерпретатора csh (9.11) или цикла for интерпретатора sh (9.12), а также воспользуйтесь ножницами и липкой лентой :-): •..' S.IS % foreach page (Pay me '$10,' 000) ? /usr/games/bannex -w "$page" | lpr -Plaser ? end He забывайте защищать специальные символы. - JP 43.12 Типографский набор В начале 80-х одна из причин роста популярности UNIX состояла в том, что эта ОС поставлялась с собственной системой типографского набора. Это была первая операционная система, которая позволяла пользователям получать качественные документы, напечатанные на лазерных принтерах. Хотя, возможно, существуют более изощренные редакторы в системах Macintosh, высокое качество печати до сих пор является достоинством UNIX. В этом параграфе вы получите представление о различных системах типографского набора, имеющихся в UNIX. troff Это предок всех систем типографского набора. Программа troff самая старая из тех, 6 которых пойдет речь. Она использует специальный язык разметки текста. Это означает, что команды типографского набора вставляются непосредственно в документ. Документы создаются с помощью обычных текстовых редакторов, таких как vi и Emacs, обрабатываются посредством команды troff (или, что более вероятно, посредством сценария интерпретатора shell, предназначенного для форматирования) и выводятся на лазерный принтер. По сути, lroff-фанпы являются скорее программами, чем традиционными текстовыми файлами. Программа troff поставляется с тремя препроцессорами, которые обрабатывают формулы (eqn), таблицы {tbl) и простые векторные рисунки (р/с). Препроцессоры формул и таблиц довольно хороши. Программа для рисования не заслуживает внимания, хотя и имеет ряд интересных возможностей. Если необходимо создать много иллюстраций (причем высокого качества), используйте такие инструменты, как FrameMaker и Interleaf, либо специальную программу по созданию рисунков. Результат вставьте в frq^-документ. Программа troff обладает многочисленными возможностями, однако воспользоваться ими непросто. Например, при написании книги применялась программа troff. Мы добавили к ней пакеты команд для создания предметного указателя, перекрестных ссылок и содержания (см. параграф 43.13). Хотя эта система имеет репутацию устаревшей, причудливой и сложной, мы пришли к выводу, что она все еще является лучшей для проведения масштабных работ, связанных с типографским набором (в частности — для подготовки книг). В свое время программа troff поставлялась с любой UNIX-системой. Сейчас все обстоит иначе, и ее необходимо покупать за отдельную плату, особенно если вы работаете в System V. К сожалению, если программа troff поставляется вместе с операционной системой, то это, скорее всего, старая и неуклюжая версия, предназначенная для работы с устаревшим фотонаборным устройством. Чтобы продлить ее существование, были созданы версии, рассчитанные на работу со всевозможными современными принтерами. Но лучше всего купить программу troff, которая не зависит от конкретного устройства. Вы увидите, что результаты будут намного лучше. Конечно, теперь, когда поставщики зарабатывают деньги на продаже программы troff, некоторые независимые разработчики, такие как SoftQuad и Elan, продают ее расширенные версии. Организация Free Foundation Software также имеет свою версию — groff (43.16). 6S8 Часть седьмая. Терминалы и принтеры
43,12 ТЕХ ТЕХ представляет собой язык типографского набора. Его разработал в 70-е гг. Дональд Кнут (Donald Knuth) главным образом для того, чтобы написать серию книг Art of Computer Programming ("Искусство программирования"). Я не являюсь поклонником языка ТЕХ и хотел бы заранее предупредить об этом читателей. С моей точки зрения, ТЕХ ненамного лучше troff, несмотря на многочисленные заверения противоположного характера. В ТЕХ, как и в troff, команды типографского набора вставляются в текст при его написании, после чего документ обрабатывается с помощью той или иной команды форматирования. Язык ТЕХ намного больше похож на язык программирования, чем на "простой язык разметки текста". Однако если синтаксис языка troff тяжеловесен, то синтаксис ТЕХ просто уродлив. Если вам приходится писать большое число программ, он может произвести хорошее впечатление. Если же вы не занимаетесь созданием программ, вам потребуется время, чтобы привыкнуть к нему. Я не нашел, что LaTEX (пакет макросов, которые делают ТЕХ похожим на Scribe; об этом речь пойдет ниже) намного лучше своего предшественника, хотя следует признать, что он проще. С точки зрения разработки собственного пакета макросов, трудно отдать предпочтение ТЕХ или troff, но какой бы язык вы ни выбрали, для решения задачи потребуется немало усилий. Наиболее значительным достоинством программы ТЕХ является набор средств для ввода формул, в чем она действительно не имеет себе равных. (Препроцессор eqn системы troff также решает эти задачи, но он уступает в плане гибкости.) Программа не содержит процессора для рисования, хотя мне встречались версии препроцессора pic программы troff, которые работали с программой ТЕХ. Тем не менее, если вам нужны иллюстрации, то их необходимо разрабатывать с помощью других инструментов и затем вставлять в ТЕХ-документ. Мне кажется, что самый большой недостаток программы ТЕХ — ее сообщения об ошибках. В программе troff почти нет сообщений об ошибках. Но в ТЕХ они непонятны или вводят в заблуждение, что еще хуже. В своих сообщениях ТЕХ обычно жалуется на сбои в пакете макросов. Вы получаете сообщение об ошибке и номер строки кода, которого никогда не видели. Да, введенные вами данные действительно вызвали ошибку, но если вы не знаете, как работает программа ТЕХ, то вряд ли догадаетесь о причине ошибки. Еще один недостаток: ТЕХ характеризуется невероятной гибкостью. К сожалению, этот показатель настолько высок, что позволяет людям с развитым интеллектом, но с плохим вкусом создавать уродливые документы. Нет никакой надобности каждому подбирать алгоритм вставки пробелов между буквами и словами. Короче говоря, ТЕХ в равной степени обеспечивает создание как прекрасных, так и безобразных документов. Следует отметить тот положительный момент, что программа ТЕХ бесплатно распространяется Американским математическим обществом. Это весьма удобно для тех, кому необходимо недорогое решение для типографского набора. [Существуют и коммерческие версии. Их пользователям оказывается мощная консультационная поддержка. — ЕК\ Scribe Невозможно не упомянуть о Scribe — действительно хорошем инструменте. Scribe представляла собой еще одну "пакетную" систему для типографского набора, подобную ТЕХ и troff Авторы Scribe чрезвычайно удачно скрыли ее сложность от конечного пользователя. Без всякой предварительной тренировки можно было быстро создать хорошо оформленный документ. Однако цену Scribe завысили настолько, что ее нельзя было признать разумной даже для рабочих станций. Это ограничило распространение Scribe только рынком рабочих станций. Использование технологии WYSIWYG для обработки документов В течение последних десяти лет на нас обрушился шквал инструментов, которые поддерживают технологию WYSIWYG (What You See Is What You Get — что вы видите, то и получаете) и предназначены для работы с документами. Благодаря режиму WYSIWYG рабочая станция или X-терминал (Ui) отображает на экране документ в близком к реальному виде. Такие инструменты хороши для создания иллюстраций. Они не совсем подходят для работы с формулами, однако Печать 689
Л3.13 некоторые из них имеют встроенные препроцессоры для этого. Недостаток WYSIWYG-ин- струментов заключается в том, что их невозможно использовать на ASCII-дисплеях. Мы испытали ряд основных WYSIWYG-пакетов для UNIX и используем некоторые из них в издательстве O'Reilly & Associates. В плане возможностей эти инструменты отчаянно пытаются превзойти друг друга. Мне кажется, что многие возможности встроены чисто в рекламных целях. WYSIWYG-инструменты на удивление слабы при работе с документами большого объема (например, с макетами книг). Да, они способны обрабатывать такие документы, но закаленные в сражениях пакетные программы (Scribe, ТЕХ и troff) справляются с этими задачами так же хорошо, если не лучше. WYSIWYG-пакеты, конечно, проще изучить, чем пакетно-ориентированные программы для типографского набора. Однако не преувеличивайте преимущества графического интерфейса. Хотя его легче освоить, чем troff или ТЕХ, я не уверен, сможет ли какая-то из WYSIWYG-программ тягаться с таким хорошо спроектированным пакетным процессором документов, как Scribe. Все WYSrWYG-процессоры являются коммерческими продуктами и, как правило, имеют высокую цену. Мне неизвестно о существовании общедоступного или бесплатного инструмента такого типа. - ML 43.13 Средства форматирования текста: nroff, troff, ditroff... Вы привыкли работать с такими настольными издательскими WYSIWYG-программами, как FrameMaker, WordPerfect, InterLeaf и т.п.? Если да, то вы, должно быть, не очень хорошо знакомы с первыми программами форматирования — nroff и troff. Такие программы не выводят на экран образ документа в законченном виде во время набора, а читают исходный файл, содержащий текст и специальные команды форматирования. Форматированный вывод направляется в файл (текстовый, PostScript либо другого формата) или прямо на принтер. Исходный текст создается и редактируется с помощью любого текстового редактора (например, vl). Издательство O'Reilly & Associates все еще использует программу troff для подготовки многих книг. Например, начало исходного файла для данного параграфа имеет следующий вид: .Ah 2520 "The Text Formatters nroff, troff, ditroff, ..." Have you used a WYSIWYG (What You See Is What You Get) desktop publishing program like FrameMaker, WordPerfect, Interleaf and so on? Then you might not have much experience with the original UNIX formatters, \fInroff\fP \fItroff\fP. .LP Instead of showing a picture of the completed document on your screen as you type, these formatters read a \fIsource file\fP full Вы можете удивленно спросить: "Зачем использовать это старье?" • Как правило, UNIX-системы поставляются с этими программами, поэтому документы, создаваемые посредством таких программ, являются переносимыми. • Исходные файлы имеют намного меньший объем, чем файлы WYSIWYG-программ. • Исходные файлы содержат обычный текст без каких-либо непечатаемых символов; их легко копировать из одной системы в другую. Например, я работаю над книгами на своем портативном компьютере с DOS, а затем переношу файлы в UNIX для форматирования. • Используемый язык форматирования имеет набор мощных возможностей, которые обеспечивают полный контроль над внешним видом страниц со стороны профессиональных наборщиков (так показывает практика издательства O'Reilly & Associates). • Для обработки текста можно использовать утилиты UNIX — grep, awlc, сценарии shell и многое другое. Это еще больше расширяет круг средств форматирования. Например, число 690 Часть седьмая. Терминалы и принтеры
Ш5 2520 после .Ah в приведенном выше примере является именем файла. Файл для каждого параграфа этой книги имеет имя, которое состоит из четырех цифр и связано с номером параграфа. С помощью написанных мною сценариев формировалась информация, печатаемая в верхних колонтитулах (номер и название параграфа). Кроме того, эти сценарии обрабатывали все перекрестные ссылки на параграфы. Я также написал программу управления макетом, так что мы могли работать над структурой книги, изменяя один-единственный файл. Все это стало возможным только потому, что программа troff не рассчитана исключительно на самостоятельную работу — мы могли задействовать всю мощь UNIX. Такие пакетные программы форматирования, как nroff к troff, не позволяют решить все задачи форматирования, однако они заслуживают внимания, поскольку полезны для решения специфических задач. - JP 43.14 Программы nroff/troff и пакеты макросов Программы nroff'и troff обладают набором встроенных команд, но по-настоящему мощными их делает возможность создания макросов. Макросы можно описать следующим образом: пакеты команд или сценарии, написанные на загадочном языке утилит nroff к troff. Макросы трудно написать и еще труднее отладить. К счастью, существует несколько общедоступных пакетов макросов. Эти пакеты содержат макросы, предназначенные для комплексной обработки текста. Например, интерактивное руководство для UNIX (so.oi) (яшл-страницы) написано с помощью пакета макросов man (so.ii), который вызывается из командной строки посредством опции -man: % nroff -man cat.l Во время написания книги мы пользовались собственным пакетом макросов, основанным на пакете ms. Это один из первых пакетов, написанных в Bell Labs, но компания AT&T не включила его в System V. Пакет ms до сих пор предоставляется системами на базе BSD, как и пакет те. В System V аналогом ms является пакет тт. - LM 43.15 От исходного файла к принтеру Путь от исходного ТЕХ- или troff-фгша к принтеру удивительно сложен. Не мешает знать, что происходит на этом пути. Эти знания не помогут вам улучшить вид документов, но пригодятся в тех случаях, когда что-то не работает как следует. Обработка ТЕХ- и //-^файлов выполняется так, как показано на рис. 43.1. Исходный k файл w Препроцессоры "Чистый" код ТЕХ ипи troff /~ Независимый от ^—Ы Конвертор Макросы Программы TEX/troff . ~Л ) устройства файл Рис. 43.1. Последовательность обработки исходного ТЕХ- ипи troff-файпа Печать 691
43.15 Отправной точкой является исходный файл, содержащий текст и команды форматирования. Сначала этот файл поступает на вход одного или нескольких препроцессоров-, обрабатывающих рисунки, таблицы и т.п. Препроцессоры важны для troff, с ТЕХ они используются не часто. Задачей препроцессора является замена команд форматирования набором более простых команд для обработки таких сложных элементов, как таблицы, формулы, и рисунки. Препроцессор интерпретирует эти команды и выводит низкоуровневые запросы языка troff, необходимые для получения желаемого результата. Наиболее распространенные препроцессоры перечислены в табл. 43.1. Таблица 43.1. Популярные препроцессоры программ troff и ТЕХ Имя pic grap tbl eqn tpic Используется с troff troff troff troff TEX Обрабатываемые объекты Рисунки Диаграммы; используется редко Таблицы Формулы Рисунки; аналогичен pic После прохода через препроцессор образуется документ, состоящий из элементарных команд, макросов и текста. Он передается программе ТЕХ или troff, которая начинает работу с того, что добавляет в начало текста файл макроопределений. Далее программа создает файл в формате, независимом от конкретного устройства. Это является общим представлением документа на языке команд низкого уровня. С независимым от устройства файлом мало что можно делать, кроме, пожалуй, проверки ошибок. Прежде чем двигаться дальше, скажем несколько слов о файле макросов. Файл макросов является не чем иным, как файлом команд troff или ТЕХ, в котором находятся макросы, установлены поля страницы, верхние и нижние колонтитулы и т.д. Такой файл вместе со всеми используемыми командами высокого уровня (например, командами формирования абзацев и заголовков) определяет вид документа. Файлы макросов для программы /л#хранятся в каталоге /usr/lib/tmac и имеют имена такой структуры: Шас.Шя. При вызове программы troff для выбора пакета макросов следует ввести опцию -тимя. Поскольку файл макросов представляет собой еще один файл команд программы troff, ничто не мешает добавить его перед исходным файлом. Таким образом, команда t.os % cat /usr/lib/tmac/tmac.s myfile.ms > foo.ms; troff foo.ms эквивалентна команде troff -ms foo.ms. Зачем может понадобиться ручная вставка файла макросов? Обычно этого не требуется, но если вы отлаживаете новый файл макросов или создаете один-два специальных макроса, добавление макроса вручную может оказаться полезным приемом. После того как troff или ТЕХ завершит обработку файла, у вас останется файл, независимый от конкретного устройства, или DVI-файл. Проблема в том, что есть две версии программы troff. независимая от устройства, или ditroff, и "старая". Ditroff — единственная программа, генерирующая независимые от устройства данные: общие выходные данные, которые легко преобразовать в файл для любого принтера. Старая программа troff генерирует данные для фотонаборного устройства С/А/Т, древнего "монстра", который, быть может, еще сохранился в музеях. Выходные данные для С/А/Т не являются независимыми: они отражают многие особенности этого устройства. Однако такие данные можно считать и независимыми от устройства — они не зависят ни от какого устройства, которое может встретиться вам! :-) Возможность создания файлов в формате, который не зависит от конкретного устройства, появилась в программе ТЕХ. DVI-файлы программы ТЕХ имеют четкую структуру и довольно элегантны. К сожалению, они полностью отличаются от аналогичных файлов программы ditroff. А если вы работаете с какой-нибудь версией troff, предоставленной независимыми разработчиками (например, с версией sqtroffфирмы SoftQuad или версией groff организации FSF), то, вероятно, имеете дело с еще одним видом независимого от устройства вывода. К сожалению, форматы независимых от устройств файлов не унифицированы. 692 Часть седьмая. Термииаяы и принтеры
43:17 Как бы там ни было, после того как программа форматирования завершит работу, ее. независимый от устройства вывод (любого вида) должен быть преобразован в команды вашего принтера. Это осуществляет постпроцессор. Имя постпроцессора зависит от типа используемого принтера, от используемой версии программы ТЕХ или troff, а также от того, где приобретено программное обеспечение. Одним из постпроцессоров является программа psroff, которая входит в состав пакета transcript фирмы Adobe и преобразует вывод программы ditroff в формат PostScript. Хотя все это может.показаться сложным,, большая часть описанных процессов незаметна для вас. Чаще всего программа troff вызывается посредством сценария, который автоматически управляет всей работой постпроцессора (и в некоторых случаях препроцессора). Однако когда нужно отладить неработающие макросы, обработку текста приходится выполнять вручную. - ML 43.16 Программа groff В Программа troff (43.12) изначально разработана для теперь уже устаревшего устройства типографского набора. Одно из основных ограничений этого устройства заключалось в возможности использования всего четырех видов шрифта на одной странице (обычно — шрифт Roman, полужирный, курсив и специальный шрифт, содержащий математические знаки и другие символы). Невозможно было получать страницы, подобные страницам нашей книги, на которых используется до восьми, видов шрифта — четыре перечисленных выше вида, особый шрифт для заголовков, а также моноширинный шрифт полужирного и курсивного начертания для текстов сценариев и примеров. Исходная программа troff была заменена независимой от устройства программой ditroff (43.1S), для которой не были характерны описанные ограничения и которая обладала рядом новых возможностей. К сожалению, в UNIX-системах, предшествовавших System V, программа ditroff не входила в комплект поставки и продавалась как отдельный продукт, поэтому некоторые системы содержат только ее старую версию. К счастью, есть выход. Программа groff организации Free Software Foundation предоставляет все замечательные возможности программы ditroff. Она включает также постпроцессоры, преобразующие ее независимые от устройства выходные данные (4J.1S) в формат PostScript. groff _ T0R 43.17 Не имеете программы nroff? Попробуйте gnroff или awf Некоторые UNIX-системы распространяются без программы nroff Ваш поставщик операционной системы может продавать эту программу отдельно как часть пакета для форматирования, который не прилагается к операционной системе. Кроме того, у вашего поставщика может не оказаться программы nroff, и тогда вам придется покупать ее у независимого поставщика, такого как Elan или SoftQuad. Для систем, в. которых программа nroff не установлена, страницы руководства поставляются в форматированном виде (для команды cat), поэтому пользователи могут читать их без nroff Проблема возникает при попытке установки коммерческих или общедоступных пакетов программ независимых разработчиков, которые не поставляют форматированные версии своей документации. Можно попробовать вручную исправить исходные nroff-фаты, но лучше этого не делать, если есть возможность получить работающую версию программы nroff. Версия программы nroff с именем gnroffют организации Free Software Foundation содержится на компакт-диске. й Другой альтернативой является программа awf — версия nroff на базе утилиты awk. Эта е"п программа предоставляет далеко не все функциональные возможности программы nroff, но довольно неплохо имитирует ее. Программа awf распознает макросы пакетов man и ms. Чтобы использовать ее, необходимо в командной строке указать имя пакета макросов (как в случае программы nroff): % awf -man cat.l - LM m gnroff H Печать 693
43.18 43.18 Как программа nroff создает полужирный и подчеркнутый шрифты и как их удалить Программа форматирования nroff создает выходные данные для строчных принтеров и алфавитно-цифровых дисплеев. Чтобы достичь такого специального эффекта, как выделение полужирным шрифтом, она выводит букву, потом символ возврата, а затем еще раз ту же букву. Образец такого вывода, как он выглядит в текстовом редакторе или при задании команды cat -v ps.07), приведен ниже: ЫАНЫЛНЫЛНЫААНААНААНАМЛНМЛНМАНМЕЛНЕАНЕАНЕ Данная строка обеспечивает вывод слова "NAME" полужирным шрифтом. Каждая буква повторяется еще три раза. Подчеркивание символов задается следующим образом: вводится символ подчеркивания, символ возврата и, наконец, буква, которая должна быть подчеркнута. Некоторые программы постраничного просмотра, в частности /m ps.04), также выполняют повторный вывод текста. Но во многих случаях требуется отменить шрифтовые выделения, которые, например, могут препятствовать поиску в форматированных страницах интерактивного руководства (см. параграф 50.03). Существует много способов устранения всех этих украшений. Проще всего воспользоваться такой утилитой, как col, colcrt или ul. • При использовании утилиты col введите команду % col -Ь < птоff_вътод > очащенний__вивод Опция -Ь указывает утилите col удалять из файла все символы возврата (а также символы, предшествующие символу возврата). Эта утилита не читает файлы, поэтому ей необходимо направлять данные из канала или, как в предыдущем примере, из файла с помощью оператора shell < (ts.oi). Утилита col присутствует как в System V, так и в BSD UNIX. В System V добавьте опцию -х, чтобы избежать замены пробелов символами табуляции. • При использовании утилиты colcrt введите команду % colcrt - nroff jbubom > очащвнний_вивод Опция - (дефис — да, это опция!) задает отмену подчеркивания. Если опустить эту опцию, утилита colcrt попытается сохранить подчеркивание путем помещения символов подчеркивания в отдельную строку. Например: Refer to Installing System V for information about installing optional software. Утилита colcrt есть только в BSD. В любом случае целесообразно использовать утилиту col. • Утилита ul читает переменную среды TERM и пытается преобразовать символ возврата (при подчеркивании и повторном выводе) в нечто понятное терминалу. Ее вызов осуществляется следующим образом: % Ul nrOff_BUBOM Опция -t терминал позволяет задавать тип терминала; она отменяет значение переменной TERM (5.W). Мне кажется, что утилита ul самая бесполезная из рассматриваемых; она претендует на разумность, но делает не всегда то, что нужно. Обе утилиты, col и colcrt, пытаются разумно обрабатывать "половинный" интервал (применяется для печати верхних и нижних индексов). Многие принтеры правильно воспроизводят половинный интервал, чего не могут делать большинство терминалов. Приводим еще одно решение рассматриваемой проблемы — простой jetf-сценарий (34.24). Ценность этого сценария в том, что вы можете усовершенствовать его или включить в больший sed-сценарий. Следующая команда редактора sed удаляет команды выделения полужирным шрифтом и подчеркиванием: s/.AH//g 694 Часть седьмая, Терминалы и принтеры
43.19 Эта команда удаляет любой символ, предшествующий символу возврата, а также сам символ возврата. В случае подчеркивания символ . соответствует символу подчеркивания, а в случае выделения полужирным шрифтом — повторно выводимому символу. Поскольку указанная команда применяется циклически, она удаляет несколько экземпляров повторяющихся символов, оставляя в каждой последовательности один символ. Заметим, что последовательность символов Лн вводится при нажатии одного сочетания клавиш — [CTRL-h]. Если вы работаете с редактором v/, для ввода этого символа пользуйтесь сочетаниями клавиш [CTRL-v] и [CTRL-h] (31.06). В редакторе Emacs применяйте [CTRL-q] с последующим [CTRL-h] (32.10). — DD, ML, из книги sed & awk издательства O'Reilly & Associates 43.19 Удаление начальных символов табуляции и прочих мелочей В параграфе 43.18 мы рассматривали способы удаления из вывода программы nroff повторно выводимых символов и символов подчеркивания. Конечно, это не единственная проблема, с которой можно столкнуться при работе с nroff. Приводим несколько приемов обработки «профайлов. Возможно, вы захотите удалить странные Escape-последовательности, которые инициируют подачу бумаги и другие функции принтера. Например, иногда в начале форматированной страницы видна последовательность А [ 9. Эту Escape-последовательность можно удалить с помощью команды редактора sed s/A[9//g Символ ESC вводится в редакторе vi путем нажатия [CTRL-v] pi.06) и [ESC]. В Emacs применяется последовательность ввода [CTRL-ql[ESCl (32.10). Число 9 является литералом. Типичная страница интерактивного руководства содержит также начальные пробелы для установки левого поля и создания отступа у большей части текста. При дальнейшем рассмотрении можно заметить начальные пробелы перед, заголовками (такими как NAME), но каждой строке обычного текста предшествует один символ табуляции. Символы табуляции могут встречаться и в тексте. Конечно, повсеместное употребление символов табуляции — неплохая идея. Что касается механических принтеров и даже современных дисплеев, гораздо быстрее напечатать символ табуляции, чем передвигать курсор (головку) на несколько пробелов. Однако из-за применения символов табуляции могут возникать проблемы, если ваш принтер (терминал) настроен неправильно или если вы пытаетесь произвести поиск в тексте. Для удаления левого поля и нежелательных символов табуляции используйте следующие две команды: s/A[ [TABJV/ S/ГГНЗУ /q Первая команда ищет любое количество символов табуляции или пробелов в начале строки. Вторая команда ищет символы табуляции и заменяет их одним пробелом. А теперь объединим все средства, в том числе сценарий для отмены подчеркивания и повторного вывода (из параграфа 43.18). Приводим сценарий sedman, реализующий все описанные трюки. #!/bin/sed -f #sedman -- удаление 'символов форматирования из страниц руководства s/.AH//g s/A[9//g s/A[ iTABllV/ s/Ш /g В результате применения этого сценария к типичной странице интерактивного руководства образуется файл, имеющий примерно следующий вид: who who NAME Печать €95 (9.] sedman
43.20 who - who is on the system? SYNOPSIS who [-a] [-b] [-d] [-H] [-1] [-p] [-q] [-r] [-s] [-t] [-T] [-u] [file] who ani i DESCRIPTION who can list the user's name, terminal line, login time, elapsed time since activity occurred on the line, and the Сценарий не удаляет ненужные пустые строки (см. параграфы 34.18, 25.11 и 25.10). - DD, ML 43.20 Вывод макроопределений программы troff При написании или отладке макросов программы troff (43.13> часто требуется просмотреть содержание макроса. Вместо того чтобы проводить поиск в файле макроопределений с помощью редактора, можно написать небольшой сценарий, использующий фильтр sed (34.24) и интерпретатор shell для выделения и вывода отдельного макроса. Такой сценарий создать нетрудно, поскольку макроопределение имеет структуру, которая хорошо подходит для распознавания с помощью регулярных выражений рем), используемых в sed. Макроопределение программы troff всегда начинается со строки .de, за которой следуют необязательный пробел и одна-две буквы имени макроса. Определение заканчивается строкой, в начале которой стоят две точки (..). 7>о#-макросы определены в пакете макросов, часто — в одном файле, расположенном в таком каталоге, как /usr/lib/tmac. Типичными пакетами макросов являются mm, ms и те, хотя маловероятно, чтобы каждая система содержала все три пакета. Пакет mm встречается главным образом в системах, производных от System V, а пакеты ms и те — в BSD-системах. Макросы man применяются почти во всех системах, поскольку они используются командой топ (so.oi). Приведем пример: % getmac -ms -LP Л" Л" The following definition of LP was found in /usr/lib/tmac/tmac.s: Л" .de LP .RT .if \\n(lT .sp \\n(PDu ,ne 1.1 .ti \\n(.iu Сценарий getmac приведен ниже. Для тех, кто не знаком.с редактором sed, в сценарий, содержащийся на компакт-диске (а также в сетевом архивном файле), включены комментарии. #!/bin/sh И# Использование: getmac -пакет макрос case $1 in -mm) file="/usr/lib/macros/mmt";; getmac -ms) file="/usr/lib/tmac/tmac.s";; -me) file="/usr/lib/tmac/tmac.e";; -man) file="/usr/lib/tmac/tmac.an";; *) echo "Usage: getmac -package macro" 1>S2; exit 1;( esac mac=$2 echo '.V" 696 Часть седьмая. Терминалы и принтеры
43.21 echo .WV'The following definition of 'Smac' was found in '$file': echo '.V" sed -n -e "/n\.de *'$2•/,/л\.\.$/p" $file — TOR, JP, адаптировано из книги UNIX Text Processing издательства Hayden Books, 1987 r. 43.21 Предварительная обработка ввода программы troff с помощью редактора sed На устройствах типа пишущей машинки (в том числе на терминале) тире набирается как пара дефисов (—).* При типографском наборе тире печатается как сплошная длинная черта. В программе troff употребляется специальное имя символа для длинного тире, но набирать \ (em неудобно, а Escape-последовательность нельзя использовать с программой nroff То же относится и к кавычкам: в случае наборного устройства используются открывающие и закрывающие кавычки (" и "), а в случае пишущей машинки — прямые кавычки (")■ В стандартной программе troff две обратные кавычки (") можно заменить открывающей кавычкой, а две одинарные кавычки (' ') — закрывающей кавычкой. Эти символы должны выглядеть так: " и ". Но было бы гораздо лучше, если бы мы продолжали набирать " и поручали выполнение рутинной работы компьютеру. Особенность программы troff состоит в том, что пробел перед каждым словом выводится тем же шрифтом, что и начало данного слова. Это означает, что если для фрагмента текста задан такой моноширинный шрифт, как Courier, пробелы на этом участке получатся более широкими, чем обычные пробелы, что может отвлекать читателя: Этот текст, набран шрифтом Courier; обратите внимание на пробелы. Эта проблема решается следующим образом. Программе troff следует указать генерировать пробелы с использованием предыдущего шрифта. Это достигается за счет вставки непробельного символа (\&) перед каждой установкой моноширинного шрифта. Нетрудно догадаться, каких усилий это потребует. Решением всех перечисленных проблем является предварительная обработка ввода программы troff с помощью редактора sed (34.24). В данном случае sed предстает в роли истинного потокового редактора, выполняющего редактирование конвейерным способом, результаты которого никогда не записываются в файл. Мы почти никогда не вызываем программу troff напрямую. Для ее запуска мы пользуемся сценарием, который организует конвейер, включающий стандартные препроцессоры (если они нужны), а также команды специальной обработки с помощью редактора sed. Сами по себе команды редактора sed довольно просты. Следующая команда заменяет два смежных дефиса одним тире: s/—Л\ (em/g Мы поставили еще одну обратную косую черту перед \ (em, поскольку в sed этот символ имеет специальное назначение. Во многих случаях не требуется применение данной команды замены. А что, если кто-то использует дефисы для рисования горизонтальной линии? Можно усовершенствовать сценарий, чтобы исключить строки, содержащие три и более смежных дефиса. В этих целях мы используем модификатор адреса 1 (34.19/. I /!s/—/\\(em/g Давайте проанализируем синтаксис этой команды. Изменилось то, что мы использовали шаблон для ограничения строк, на которые влияет команда замены, и употребили оператор ! для обращения смысла операции сравнения с шаблоном. В приведенной команде говорится: "Если найдется строка с тремя смежными дефисами, редактирование не применять". К остальным строкам команда замены будет применяться. * Машинистки часто используют три дефиса для ввода длинного тире и два дефиса для ввода короткого тире. Печать 697
4122 Аналогично, чтобы решить проблему, возникающую при смене шрифта, можно использовать редактор sed для поиска всех строк, соответствующих \f (CW, \f (CI и \f (CB, и вставки \& перед ними. Это можно записать так: s/\\f (C[WIB]/\\\SS/g Для решения проблемы, связанной с открывающими и закрывающими кавычками, потребуется более сложный сценарий, поскольку необходимо учесть множество различных случаев. Нужно сделать sed достаточно "умным", чтобы он заменял двойные кавычки открывающей кавычкой только в начале слов и закрывающей кавычкой только в конце слов. Подобный сценарий представлен ниже. Вероятно, его можно сократить, разумно применяя синтаксис регулярных выражений с операторами \ ([...] \) (Н.щ. Полный вариант сценария приведен для обеспечения полной ясности. s/""/'7 3/"$/"/ з/"? /"? /д s/"?$/''?/ s/ "/ -Vg s/" /" /g s/ГГШ'ЛТлвГ'/д з/'1тд1/' '1Тлв1/а s/")/' ')/g s/"]/' M/g s/l"/('7g s/\['7\["/g s/";/";/g s/":/":/g s/,'7,"/g s/",/ ",/g s/\.'7.\\\s"/g s/»\./".\\U/g з/"\\(ет/''\ \(em/g s/\\(em'7\\(em4 Vg И Предыдущий пример демонстрирует, к каким ухищрениям приходится прибегать, чтобы предусмотреть все случаи применения кавычек. Как решить другую проблему, о которой речь шла выше, подумайте сами. На компакт-диске содержится более полный сценарий ckanup.sed предварительной обработки, который написан для sed и годен для использования в среде troff (возможно, с небольшими изменениями). Наряду с проведением замен, рассмотренных выше, этот сценарий сокращает межсимвольный интервал в многоточиях (...) и запрещает редактирование текста между некоторыми ?/-<#-макросами (и.щ. - TOR, LM 43.22 Преобразование текстовых файлов в формат PostScript Если все, что имеется в распоряжении, — это принтер типа "ромашка" или строчный принтер, скорее всего, текстовому файлу, посылаемому на такое устройство, не потребуется модификация. Однако сегодня принтеры указанных типов считаются пережитками прошлого. Процесс преобразования текста в формат PostScript (если принтер поддерживает такой формат) является довольно сложным. Такое преобразование может осуществляться несколькими способами. Первый — довольно грубый, но вполне эффективный. Предположим, у вас есть работающая версия программы troff с макросом ms. Вот сценарий: # имя сценария, запускающего программу troff roff=lw # выбор пакета макросов macros=-ms 698- Часть седьмая. Терминалы и принтеры
43.23 sed -e ' li\ .DS\ .in 0\ .ft CW\ .ps 10\ .vs 12 s/\\/\\e/g s/V\\W $a\ .DE ' | $roff $macros Как работает этот сценарий? Он заключает текст в макросы tmff, которые обеспечивают печать текста моноширинным шрифтом без выравнивания. При работе с полями, новыми строками, номерами страниц и т.д. данный сценарий использует пакет макросов ms. ■SW-сценарий оформляет текст таким образом, чтобы печаталось все, что могло бы вызвать затруднения у программы tmff, в частности символы обратной косой черты и строки, начинающиеся с точки или одинарной кавычки. Данный сценарий может вывести на печать даже пакет /лэ^-макросов. Мне нравится этот сценарий. Он крайне прост, и поскольку в нем используются инструменты UNIX при выполнении тех задач, для решения которых они и были разработаны, настоящий сценарий является хорошим средством демонстрации идеологии UNIX. Если вы немного знаете язык утилит trcff, то сможете приспособить его для решения конкретных задач. Существуют и другие решения. Возможно, лучшим из них является использование программы enscript, которая входит в состав пакета transcript фирмы Adobe. Эта программа является коммерческой, поэтому мы не смогли поместить ее на компакт-диск. Но, по крайней мере, мы можем направить вас по нужному пути. ^^^щ Одним из бесплатных вариантов является программа pstext Дэна Джадда (Dan Judd). Несмотря I (Л) I на небольш°й объем, она предоставляет множество возможностей. Программа эмулирует ^_ ^- X стандартный строчный принтер: она интерпретирует символы прогона бумаги как разделители jsfeT7 страниц, правильно обрабатывает символы возврата и табуляции, позволяет изменять поля, количество строк на странице и т.д. Синтаксис вызова программы pstext следующий: (рг47.02 % pstext опции файл1 файв2 ... | lpr Эта программа поддерживает множество опций. Самые важные из них следующие: -1 Альбомная ориентация страницы (печать вдоль длинной стороны страницы); установка по умолчанию — книжная ориентация -id Размещение на одном листе двух страниц с альбомной ориентацией -d Размещение на одном листе двух страниц с книжной ориентацией -s р Использование шрифта, размер которого — р (в пунктах) -f имя Использование шрифта с указанным именем -п л Печать п строк на странице - ML 43.23 Программа psselect: печать отдельных страниц из PostScript-файла Программа psselect позволяет задать печать отдельных страниц из PostScript-файла. Она является частью коллекции Ангуса Дагтана (Angus Duggan) PSUtils — группы утилит для работы с PostScript-файлами. Программа psselect создает второй PostScript-файл, который можно напечатать с помощью команды lpr или 1р (43.<П) (если ваш принтер понимает язык PostScript). Следует отметить, что данная программа удобна в использовании. Самый простой способ вызова следующий: % psselect -рсписок_страниц ввод вывод где список_страниц — это список разделенных запятыми номеров страниц и (или) диапазонов номеров страниц с дефисами. Отсчет страниц ведется с 1 (первая страница документа). Печать 699
43.24 Если перед номером поставить символ подчеркивания, подсчет.страниц будет производиться в обратном порядке — от конца документа. Программа psselect не пытается найти и обработать номера страниц, которые могут содержаться в документе. Чтобы выбрать страницы от 1 до 16 файла book.ps, введите: % psselect -pl-16 book.ps firstl6.ps Опция -е обеспечивает вывод на печать только четных страниц, а -о — нечетных. (При использовании опции -е или -о необходимо также указывать Опцию -р.) Чтобы задать печать страниц в обратном порядке, добавьте опцию -г. Программа psselect работает только с теми PostScript-файлами, которые соответствуют соглашениям фирмы Adobe о структуре документов. Мы не будем подробно рассматривать эти соглашения, скажем только, что отвечающие им PostScript-файлы являются скорее исключением, чем правилом. К сожалению, язык PostScript слишком гибкий. В связи с этим невозможно написать универсальные программы для выделения страниц (а также подобные программы), не освоив всего языка. К счастью, Ангус написал несколько сценариев, которые преобразуют PostScript-файлы, сгенерированные различными текстовыми процессорами, в другие распространенные форматы. Все сценарии написаны на языке Perl (j7.oi). Приводим краткий список того, что имеется: Имя fixdlsrps fixfmps fixmacps fixpsditps fixpspps fixscribeps fixtpps fixwfwps fixwpps fixwwps Назначение Преобразование файлов формата DviLaser/PS Преобразование файлов формата FrameMaker Преобразование файлов формата Macintosh Преобразование pidit-фяйлов формата TranScript Преобразование файлов формата PS Print Преобразование файлов формата Scribe Преобразование файлов формата Tpscript Преобразование файлов формата Word for Windows Преобразование файлов формата WordPerfect Преобразование файлов формата Windows Write Так, чтобы получить нечетные страницы документа fmdoc.ps, созданного в программе PageMaker, ведите следующую команду: % fixfmps < fmdoc.ps - ML, JP, AD psselect -o -pi- > oddpages.ps 43.24 Другие PostScript-утилиты PSUtih Пакет Ангуса Даггана PSUtils наряду с утилитой psselect (43.23) содержит рад интересных инструментов- Хотя эти инструменты имеют более узкую область использования, они заслуживают внимания, так как очень полезны в ряде случаев при печати. Например, они помогают автоматизировать печать брошюр или бюллетеней. В данный параграф мы включили лишь краткий обзор инструментов пакета PSUtils. Более подробную информацию вы найдете в документации Ангуса. Epsffit Размещает файл формата EPSF (Encapsulated PostScript) в пределах заданной рамки. Позволяет центрировать изображение, вращать его, изменять коэффициент сжатия и т.п. extractres Извлекает из PostScript-документа ресурсы (шрифты, наборы процедур, шаблоны, файлы и т.п.), помещает их в отдельный файл и вставляет соответствующие комментарии %%lncludeResource в начало документа. Это обеспечивает безопасность при перекомпоновке документа с помощью утилит psnup и pstops. includeres Помещает в PostScript-документ ресурсы (шрифты, наборы процедур, шаблоны, файлы и т.п.), указанные в комментариях %%IncludeResource (см. выше). 700 Часть седьмая. Терминалы н принтеры
43.25 psbook Компонует страницы на листе таким образом, что при его фальцовке (сгибании) получается тетрадь. psmerge Объединяет PostScript-документы в единый документ. Работает только при определенных условиях: документы должны быть созданы в одном и том же приложении для печатающих устройств с одинаковой конфигурацией. psnup Выполняет перекомпоновку PostScript-документа, масштабируя и ориентируя страницы таким образом, что на одном листе печатается несколько страниц, вследствие чего получается буклет. Утилита psnup совместно с psbook делает относительно простым производство буклетов (и даже книг среднего размера). Как и в случае psbook, для приведения PostScript-файла в надлежащий вид может понадобиться такой препроцессор, как flxfmps (43.23). psresize Изменяет масштаб и центрирует документ для листов различных размеров. Например, преобразует документ формата letter в документ формата А4. pstops Универсальная утилита для перекомпоновки страниц PostScript-документа. Позволяет выбирать отдельные страницы, размещать на одном листе несколько страниц различной ориентации и т.д. - JP, ML, AD 43.25 Пакет создания переносимой графики Существуют десятки форматов графических файлов: tiff, PICT, gifw т.п. Помимо них разработан ряд форматов, для вывода изображений на различных устройствах, для печати на различных принтерах, а также ряд внутренних форматов графических программ. Разнообразие форматов значительно усложняет перенос графических файлов с одной платформы на другую (или из одной программы в другую). При этом может потребоваться специальный фильтр, преобразующий изображение из одного формата в другой. И Пакет netpbm поддерживает многие варианты преобразования одного графического формата в другой. Этот пакет основан на переносимом графическом пакете pbmplus Джеффа Посканцера (Jef Ppscanzer). Пользователи пакета pbmplus в Internet объединились для его net/Am модернизации, вследствие чего и возник пакет netpbm. Идея переносимости состоит в следующем: существует набор основных графических форматов, в которые можно преобразовать изображения всех (или почти всех) остальных форматов, после чего можно выполнить и обратное преобразование. Это намного проще, чем иметь для каждого формата конверторы, действующие в обоих направлениях. К основным форматам относятся pbm, pgm и ppm: portable bitmap (переносимая битовая карта), portable graymap (переносимая полутоновая карта) и portable pixmap (переносимая пиксельная карта). (Битовая карта — это двухмерное представление изображения; полутоновая карта содержит дополнительную закодированную информацию о фадациях серого для каждого бита; в пиксельной карте используется кодирование цвета для каждого бита.) Имя pnm является родовым именем для каждого из трех переносимых взаимозаменяемых форматов (я соответствует any — любой). Профаммы, которые поддерживают все три формата, называются профаммами типа anymap. Пакет netpbm содержит более сотни профамм преобразования, которые можно разделить на три категории: • Профаммы, преобразующие фафические файлы в один из /w/я-форматов. Например, если tiff-файл необходимо преобразовать в формат PostScript, сначала нужно запустить профамму tifftopnm: % tifftopnm Hobbes.tiff > Hobbes.pnm • Профаммы, преобразующие изображения из /w/я-формата в другой формат. Например, если файл Hobbs.pnm нужно преобразовать непосредственно в формат PostScript, можно использовать профамму pnmtops: % pnmtops Hobbes.pnm > Bobbes.ps Печать 701
43.2S • . Программы, предназначенные для манипулирования изображениями в формате рпт. Например, если нужно вырезать часть изображения, можно сначала воспользоваться программой pnmcut, а затем преобразовать файл в формат PostScript для печати: % tifftopnm Hobbes.tiff > Hobbes.pnm % pnmcut 10 10 200 200 Hobbes.pnm > Hobbes.cut % pnmtops Hobbes.cut > Hobbes.ps % lpr Hobbes.ps Выполнение тех же операций можно задать в одной командной строке (не засоряя диск промежуточными файлами): % tifftopnm Hobbes.tiff | pnmcut 10 10 200 200 | pnmtops | lpr Я часто создаю растровые изображения в формате XII bitmap p.3i), сканированные из газет и журналов. Сначала я сканирую картинку на Macintosh и сохраняю ее в формате tiff или PICT. Затем я передаю файл по fip_ о.зз) в нашу UNIX-систему и преобразовываю его в формат рпт, после чего запускаю программу pbmtoxbm для преобразования в формат XI1 bitmap. Если картинка слишком большая, я обрабатываю промежуточный рпт-файп в программе pnmscak. Когда изображение имеет неправильную ориентацию, я запускаю программу pnmrotate или pnmflip перед тем, как преобразовать рпт-файл в формат XI1 bitmap. Пакет netpbm содержит слишком много программ, чтобы описывать их подробно. О некоторых форматах вы, возможно, никогда не слышали. Поэтому мы решили офаничиться списком программ. В табл. 43.2 перечислены программы преобразования, а в табл. 43.3 — программы для редактирования файлов изображений. Таблица 43.2. Программы преобразования графических форматов Формат Andrew Toolkit (растровый объект) ASCII AT&T 4425 (режим 132 колонки одноименного терминала) Atari Degas (файлы с расширением .pi 1) Atari Degas (файлы с расширением .pi3) Atari Spectrum (несжатый файл) Atari Spectrum (сжатый файл) AutoCAD (база данных или слайд) Biorad (файл одноименного конфокального микроскопа) CMU (файл растрового изображения одноименной оконной системы) DDIF DEC LN03+ Sixel DEC Sixel Display Pixel Data (терминал BBN BitGraph) Encapsulated PostScript (растровое изображение для предварительного просмотра) Epson (графика для одноименного принтера) Face (файл одноименной профаммы фирмы Bennel Yee) Fast PostScript (файл одноименной профаммы) FITS (Flexible Image Transport System) GEM (файл с расширением .img) Gemini 10X (графика для одноименного принтера) GIF В pnm-формат atktopbm asciitopgm piltoppm pUtopbm sputoppm spctoppm sldtoppm bioradtopgm cmuwmtopbm ybmtopbm fitstopnm gemtopbm giftopnm Из опт-формата pbmtoatk pbmtoascii pbmto4425 ppmtopil pbmtopi3 ppmtoacad pbmtocmuwm pnmtoddif pbmtoln03 ppmtosixel pbmtobbnbg pbmtoepsi pbmtoepson pbmtoybm pbmtolps pnmtofits pbmtogem pbmtolOx ppmtogif 702 Часть седьмая. Терминалы и принтеры
43.25 Формат Gould (файл одноименного сканера) GraphOn (сжатый файл) Grayscale (необработанный графический файл) Group 3 (файл одноименного факса) HIPS IFF ILBM (Amiga) Img-whatnot Lisp Machine (растровое изображение) Macintosh PICT MacPaint MGR (растровое изображение) Mitsubishi S340-10 (фафика для одноименного принтера) Motif UIL (файл пиктограммы) MTV или PRT (выходные файлы одноименных профамм) NCSA ICR PCL (файл для принтера HP LaserJet) PCL (файл для принтера HP PaintJet) PCL (файл для принтера HP PaintJet XL) PCX PK (упакованный шрифт) Photo-CD Image Plot (файл одноименной программы) PostScript (использует профамму ghostscript) PostScript (требует ручной корректировки исходного файла) Pnntronix (фафика для одноименного принтера) QRT (выходной файл одноименной профаммы) RGB (необработанный фафический файл) SGI (файл изображения) Solitaire SPOT (изображения со спутника) Sun (пиктофамма) Sun (растровое изображение) TIFF Truevision Targa Usenix FaceSaver(tm) Windows (растровое изображение для PC) XlO (битовая карта) XI0 (дамп окна) X11 (битовая карта) XI1 (дамп окна) XI1 (пиксельная карта) XI1 (файл игры "puzzle") Xerox doodle brush Xim В рпт-формат gouldtoppm rawlopgm g3topbm hipstopgm ilbmtoppm imgtoppm lispmtopgm picttoppm macptopbm mgrtopbm mtvtoppm pjtoppm psxtoppm pktopbm hpcdtoppm psidtopgm pstopnm qrttoppm rawtoppm sgitopnm sirtopnm spottopgm icontopbm rasttopnm tiffiopnm tgatoppm fitopgm bmptoppm xbmtopbm xwdtopnm xbmtopbm xwdtopnm xpmtoppm brushtopbm ximtoppm Из рпт-формата pbmtogo pbmtog3 ppmtollbm pgmtolispm ppmtopict pbmtomacp pbmtomgr ppmtomitsu ppmtouil ppmtoicr pbmtolj ppmtopj ppmtopjxl ppmtopcx pbmtopk pbmtoplot pnmtops pbmtoptx pnmtosgi pnmtosir pbmtoicon pnmtorast pnmtotiff ppmtotga pgmtofs ppmtobmp pbmtoxl(hm pbmtoxbm pnmtoxwd ppmtoxpm ppmtopuzz Печать 703
43.25 Формат XV (формат броузера Visual Schnauzer) YUV Abecas Video System, байты YUV Abecas Video System, триплеты (MPEG/JPEG) Zeiss (файл одноименного конфокального Zinc (растровое изображение) Переносимая битовая карта Переносимая пиксельная карта Переносимая полутоновая карта Текст Три переносимых полутоновых карты Неизвестный микроскопа) В рпт-формат xvminitoppm yuvtoppm yuvsptittoppm zeisstopnm pbmtext rgb3toppm anytopnm Из рпт-формата ppmtoyuv ppmtoyuvsplit pbmtozinc pgmtopbm pgmtoppm pbmtopgm ppmtopgm ppmtorgb3 Таблица 43.3. Программы манипулирования pnm-файпами Программа Назначение pbmclean pbmlife pbmmake pbmmask pbmpscale pbmreduce pbmupc pgmbentley pgmcrater pgmedge pgmenhance pgmhist pgmkemel pgmnoise pgmnorm pgmoil pgmramp pgmtexture pnmalias pnmarith pnmcat pnmcomp pnmconvol pnmcrop pnmcut pnmdepth pnmenlarge pnmfile Инвертирует пиксели битовой карты, не имеющие идентичных соседних пикселей Применяет к датя-иэображению правила игры "Жизнь" Конвея Создает пустую битовую карту заданного размера Создает черно-белую маску изображения Увеличивает рб/я-изображение, сглаживая контуры Читает рб/я-изображение и уменьшает его в N раз Создает битовую карту для универсального штрих-кода Создает эффект Бентли Создает кратерную поверхность путем фрактальной прорисовки Находит контуры дат-изображения Выделяет контуры pgm-изображения Печатает гистограмму значений серого в дат-изображений Генерирует массив значений, используемый программой pnmconvol Создает /igm-изображение, имитирующее белый шум с помощью градаций серого Выполняет нормализацию контраста дат-изображения Создает имитацию рисунка маслом для дат-изображения Генерирует шкалу градаций серого Генерирует различные текстуры в формате pgm Осуществляет сглаживание дат-изображения (удаляет "ступеньки") Генерирует дат-иэображение путем выполнения арифметических операций над двумя изображениями Добавляет одно дат-изображение в конец другого Накладывает одно дат-изображение на другое Заменяет каждый пиксель дат-изображения взвешенным средним значением соседних пикселей Обрезает дат-изображение по контуру Вырезает прямоугольную область из дат-изображения Изменяет максимальное значение шкалы цвета в дат-изображений Читает дат-изображение и увеличивает его в N раз Создает описание дат-изображения 704 Часть седьмая. Терминалы и принтеры
Ш5 Программа Назначение pnmflip pnmgamma pnmhistmap pnmindex pnminvert pnmmargin pnmnlfilt pnmnoraw pnmpad pnmpaste pnmrotate pnmscale pnmshear pnmsmooth pnmtile ppm3d ppmbrighten ppmchange ppmdim ppmdist ppmdither ppmflash ppm/orge ppmhist ppmmake ppmmix ppmnorm ppmntsc ppmpat ppmquant ppmquantall ppmqvga ppm relief ppmshift ppmspread ppmtomap Выполняет одну или несколько операций зеркального отражения узлю-изображения Выполняет гамма-коррекцию дот-изображения Строит гистограмму значений для pgm- или дот-изображения Создает набор уменьшенных изображений с названиями (индекс) Инвертирует дот-изображение Добавляет рамку к дот-изображению Применяет разнообразные нелинейные фильтры Преобразует дот-изображение в незакодированный формат Добавляет рамки к дот-изображению Вставляет прямоугольную область в дот-изображение Поворачивает дот-изображение на заданный угол Масштабирует дот-изображение Выполняет наклон дот-изображения на заданный угол по горизонтали Осуществляет затушевывание изображения Заполняет копиями дот-изображения область указанного размера Накладывает одно дот-изображение на другое со смещением по горизонтали (для получения 3-мерного изображения, просматриваемого с помощью специальных очков) Изменяет насыщенность и яркость изображения в HSV-шкале Заменяет один цвет другим Затемняет дот-изображение (подобна программе ppmbrighten) Выполняет упрощенное преобразование цветных изображений в полутоновые Преобразует пиксели цветного изображения для имитации полутонов Повышает яркость изображения (подобна программе ppmbrighten) Генерирует фрактальные изображения туч, планет и звездного неба Печатает гистограмму цветов дот-изображения Создает пиксельную карту заданного размера и цвета Смешивает два дот-изображения Выполняет нормализацию контраста дот-изображения Придает дот-изображению вид телевизионного кадра (приглушение каждой второй строки) Создает красивую демонстрационную картинку Уменьшает глубину цвета дот-изображения до указанного значения Запускает программу ppmquant для целого пакета файлов, вследствие чего они приобретают одинаковую глубину цвета Задает восьмибитовую глубину цвета Применяет рельефный фильтр Лапласа к дот-изображению Задает случайный сдвиг строк изображения вправо и влево (эффект отражения в воде) Смещает пиксели на произвольное расстояние (эффект расфокусированного изображения) Генерирует карту цветов, используемых в дот-изображении Пакет netpbm распространяется вместе с набором общедоступных растровых изображений. Мы поместили эти изображения на компакт-диск. - LM, JP
Часть восьмая Программирование в среде shell Интерпретатор shell является главным инструментом UNIX. Именно благодаря возможностям интерпретатора shell — в том числе таким, как каналы, фильтры и переадресация ввода-вывода, а также концепция среды, где маленькие программы выполняют большие задачи, работая совместно, — становится возможной разработка программ, выполняющих разнообразные задачи, которые возникают в повседневной практике пользователей UNIX. Некоторые начинающие пользователи UNIX программируют исключительно на языке Perl. Действительно, этот язык позволяет решать многие задачи, которые без него были бы трудновыполнимыми или неразрешимыми. Кроме того, Perl- сценарии работают в других операционных системах. Однако мы верим, что еще осталось место для традиционных средств программирования UNIX. Независимо от того, создаете вы программы для shell или нет, следующие четыре главы окажутся полезными при интерактивной работе с интерпретатором. - TOR, JP 23*
44 Программирование для начинающих 44.01 Каждый должен иметь навыки программирования в shell Одна из замечательных особенностей UNIX связана с тем, что эта система состоит из небольших утилит ("кирпичиков"), наподобие cat и grep, запускаемых по приглашению интерпретатора shell. Каналы, средства переадресации, фильтры и т.д. обеспечивают возможность комбинирования утилит для выполнения невероятно большого количества задач. Значительным достоинством shell как среды для выполнения программ является возможность поместить в один файл команды, которые вводятся по приглашению интерпретатора shell, a затем запускать его, всего лишь указывая имя. Для решения разнообразных задач можно прибегнуть к еще одной хитрости: создать новые программы, в которых требуемые программы (в том числе сценарии) сочетаются определенным вами способом. Если какая-то программа не работает должным образом, напишите Сценарий, который будет делать то, что вам нужно. Пользователям UNIX, которые ежедневно работают с интерпретатором shell, не придется изучать совершенно новый язык программирования. Им будет достаточно освоить только некоторые особенности и приемы. Данная глава посвящена приемам программирования, которые пригодятся вам, даже если вы не будете заниматься написанием программ. Например, в ней рассматриваются циклы и условные конструкции, которые удобно включать в командную строку. (В параграфах этой главы предполагается, что вам приходилось писать программы на каком-либо языке или вы знакомы в общих чертах с основными понятиями программирования. Если это не так, можете обратиться сначала к специальной литературе по программированию в среде shell.) Базовые понятия, которые должен знать начинающий программист, уже раскрывались в других главах. Приводим перечень параграфов в том порядке, который делает их похожими на своеобразный учебник. Как написать простой сценарий, вы узнаете в параграфе 44.02. Включению в shell-сценарий сценариев на других языках (например, sed или awlc) посвящен параграф 44.14. Понятие интерпретатора shell раскрывается в параграфе 44.03. Чтобы понять, каким образом ваша система выполняет файлы, прочтите параграф 44.04. Сведения о переменных среды и интерпретатора shell приведены в параграфах 6.01 и 6.08. Команда echo описана в параграфе 8.06. Как в интерпретаторе shell защитить специальные символы с помощью кавычек и обратной косой черты, рассказывается в параграфе 8.14. В параграфе 44.05 речь пойдет о способах проверки условий с помощью оператора case, а в параграфе 44.06 — о шаблонах оператора case. Использование вывода одной команды в качестве аргумента другой с помощью механизма подстановки результатов выполнения команд рассматривается в параграфе 9.16. Программирование для начинающих 709
44.02 • Как посредством кода завершения определить, сработала программа или нет, вы узнаете в параграфе 44.07. • Параграфы 44.08 и 44.09 посвящены вопросам проверки кода завершения и выполнения различных действий, в зависимости от того, успешно сработала программа или нет. • О цикле из нескольких команд, а также об использовании других команд для управления циклом рассказывается в параграфе 44.10. • Тема параграфа 44.11 — установка кода завершения сценария. • В параграфе 44.12 рассматривается обработка сигналов прерывания (например, [CTRL-с]), а также других сигналов, в параграфе 44.13 — процесс чтения ввода с клавиатуры, а в параграфе 44.15 — обработка аргументов командной строки (опций, имен файлов и т.д.). • Вопросам пошаговой обработки аргументов, а также просмотру любого списка слов с помощью цикла for посвящены параграфы 9.12 и 44.16. • Обработка аргументов посредством команд while и shift описана в параграфе 44.17. • Унифицированный и переносимый способ обработки аргументов командной строки с помощью команды getopt предложен в параграфе 44.18. • В параграфе 44.19 рассказывается, как устанавливать опции интерпретатора shell и командной строки с помощью команды set. • Проверка файлов и строковых переменных с помощью команды test освещена в параграфе 44.20. • Как избежать конфликтов при выборе имени новой команды, вы узнаете в параграфе 44.21. • Темой параграфа 44.22 является определение имени выполняемой программы и его использование в сценарии. • Принципы использования "подпрограмм", которые могут изменить текущую среду, изложены в параграфе 44.23. В настоящей главе речь пойдет о программировании в среде Bourne shell. Позднее вы узнаете, почему С shell плох для программирования (47.02). Замечание относительно версий команд: к сожалению, в различных версиях UNIX одна и та же команда может иметь различные опции. Версии интерпретатора Bourne shell отличаются друг от друга. Например, в одних версиях команда test (44.20) поддерживает опцию -х (проверка права на выполнение файла), а в других — нет. Как указано в параграфе 46.10, некоторые версии команды echo поддерживают опцию -я, указывающую не выводить символ новой строки в конце текущей строки, а другие требуют для этого ставить в конце строки \с. И т.д. и т.п. Там, где есть различия между версиями команд, используются команды Berkeley UNIX. Если команда не работает в вашей системе, обратитесь к документации или к соответствующей странице руководства по интерпретатору sh. - JP 44.02 Создание простого сценария Сценарий — это не более чем сложная командная строка, сохраненная в файле. Предположим, вам нужен компактный список всех пользователей, зарегистрированных в вашей системе. Получить его позволит следующая команда: % who | cut -d-8 | sort -u | pr -11 -8 -w78 -t Список зарегистрированных пользователей должен быть разбит на столбцы и выглядеть примерно так: abraham appleton biscuit Charlie charlott fizzle howard howie hstern jerry kosmo linda ocshner peteraon root ross sutton yuppie 710 Часть восьмая. Программирование в среде shell
4102 Мы использовали четыре команды UNIX, соединенные каналами. 1. Команда who (Sim) формирует список всех пользователей. 2. Команда cut -с 1-8 (З5.щ вырезает из вывода команды who столбцы 1—8 — имена пользователей. Если в вашей системе нет команды cut, используйте команду colrm 9 (З5.ы). 3. Команда sort -и (Зб.об) располагает имена по алфавиту и исключает имена пользователей, зарегистрировавшихся более одного раза. 4. Команда рг -11 -8 -w78 -t (35.17,43.07) выводит список имен пользователей в 8 столбцов, при этом длина строк составляет 78 символов. (11 — это строчная буква L с последующей цифрой 1.) Если вам часто необходим список пользователей, лучше пользоваться командой типа % loggedin Вот как создается такая команда. 1. С помощью вашего любимого текстового редактора (Emacs, v/ или любого другого) создайте новый файл с именем loggedin. 2. Если ваша система поддерживает специальную запись # I (44.04), в начале файла сценария должна находиться следующая строка: #!/bin/sh В противном случае первую строку оставьте пустой. (Если первая строка сценария пуста, большинство интерпретаторов shell запустят Bourne shell, чтобы прочесть его. Дополнительную информацию вы найдете в параграфах 45.02 и 45.06.) Я считаю, что во второй строке сценария должен находиться комментарий, в котором поясняется, каковы функции данного сценария. (При желании для комментария можно отвести несколько строк.) Комментарий начинается с символа #, после которого все символы строки игнорируются: # loggedin - список зарегистрированных пользователей в 8 столбцах, каждое имя # приводится один раз Поместите следующие команды в третью строку, как вы делали это в командной строке: who | cut -cl-8 I sort -u I pr -11 -8 -w78 -t (Я уже объяснял, что вместо команды cut может понадобиться команда colrm.) 3. Сохраните файл и выйдите из редактора. Сценарий готов. 4. Далее необходимо сделать сценарий исполняемым. Команда chmod (22.07) предназначена для изменения прав доступа к файлу. Знак + перед х делает файл исполняемым: % chmod +x loggedin 5. Если в вашей учетной записи указан интерпретатор С shell, вам придется переустановить таблицу поиска команд. Для этого введите rehash 4.02 % rehash 6. Наконец, испытайте сценарий. Достаточно ввести его имя и нажать клавишу [RETURN]: % loggedin Если сценарий не запускается, возможно, ваш текущий каталог не значится в последовательности поиска интерпретатора shell. В таком случае попробуйте следующее: % ./loggedin Если сценарий все равно не работает, а его первая строка начинается с #!, проверьте правильность путевого имени интерпретатора Bourne shell в этой строке (например, /bin/sh). Программирование для начинающих 711
44.03 7. Чтобы запустить сценарий из любого другого каталога, кроме текущего, или же чтобы сценарий могли использовать другие программы и сценарии, необходимо поместить его в каталог, указанный в вашей последовательности поиска (s.07>. Если вы единственный потенциальный пользователь сценария, вам следует поместить его в персональный каталог bin (4.02). Иначе необходимо узнать у системного администратора, есть ли каталог для локальных команд. -JP 44.03 Что же такое shell? Shell является программой, которая интерпретирует командные строки и запускает другие программы. Другое название shell — интерпретатор команд. В настоящем параграфе описаны два основных вида интерпретатора, а также рассматриваются их запуск и принципы работы (поиск программ, чтение файлов сценариев). Как интерпретатор shell запускает другие программы При запуске каждой программы интерпретатор shell выполняет несколько действий. Если интерпретатор читает имена команд с терминала (интерактивно), он выводит приглашение (% или $) и ждет, когда вы что-нибудь введете. Затем shell читает командную строку (например, cat -v afile bfile >cfile), интерпретирует (i.oi, s.os) и выполняет ее. По завершении работы команды (если только она не является фоновой (1.26,1.27)) интерпретатор готов читать следующую командную строку. Выполнение команд в интерактивном режиме или из сценария Интерпретатор shell может читать командные строки либо с терминала, либо из файла. Файл, в который помещена командная строка, называется сценарием (44.ш). Shell обрабатывает сценарий так же, как и команды, вводимые с терминала (хотя при этом он не выводит приглашение % или $). Этой информации достаточно, чтобы понимать, как писать простые сценарии: достаточно поместить команды в файл и передать их интерпретатору shell. Впрочем, существует еще целый ряд программных конструкций, которые позволяют писать более мощные программы, чем набор команд, выполняемых обычно в интерактивном режиме. Типы интерпретаторов shell В UNIX существует два основных типа shell: • Интерпретатор С shell (csh) особенно хорош для работы с терминала. Он может читать сценарии и имеет ряд возможностей, полезных для программистов. К сожалению, csh не лишен ошибок (47.02), затрудняющих программирование. * Интерпретатор Boiirne shell (sh) и ему подобные используются главным образом для Программирования. (В некоторых новых s/г-подобных интерпретаторах, в том числе в ksh и bash (1.08), сочетаются удобные интерактивные возможности С shell и синтаксис Bourne shell.) Последовательности поиска команд Как указывалось в параграфе 8.07, когда интерпретатор shell пытается запустить команду, которая не встроена в него, он просматривает список каталогов, называемый последовательностью поиска. UNIX-системы имеют стандартные каталоги (/bin n/usr/bin), которые содержат стандартные UNIX-программы. Данные каталоги включены почти во все последовательности поиска. Если вы регулярно программируете на языке интерпретатора shell, вам следует создать каталог для исполняемых файлов. Как правило, такой каталог называют bin и помещают в начальный каталог (см. параграф 4.02). Мы испопьзуем Bourne shell .Серьезные программисты, как правило, пишут сценарии для интерпретатора Bourne shell. Так делаем и мы. " 712 Часть восьмая. Программирование в среде shell
44-04 Новые версии Bourne shell обладают такими, возможностями, как поддержка функций (io.o9) и команды unset, которые отсутствовали вплоть до. Bourne shell версии 7. Большинство сценариев, включенных в. книгу, написаны так, что могут работать во всех версиях интер- i претатора Bourne shell. Поэтому в целях обеспечения переносимости они не снабжены новыми возможностями. Советуем читать остальные параграфы этой части, сидя за терминалом, чтобы сразу выполнять предлагаемые примеры. Если в вашей учетной записи указан Bourne shell или один из производных интерпретаторов (ksh, bash и т.д.), в качестве приглашения будет выведен знак доллара. Если же используется другой интерпретатор, запустите Bourne shell, набрав sh. Приглашение должно измениться и выглядеть так: $. Вы будете работать с интерпретатором Bourne shell, пока не введете [CTRL-d] в начале строки: % sh $ $ ...Ввод команд. . . $ |CTRL-dj % - JP 44М Проверка механизма выполнения сценариев С помощью записи #! можно сообщить системе UNIX, какой интерпретатор должен выполнять команды из данного файла.* Если же система не распознает эту запись, вам необходимо узнать, как заставить ее читать сценарии с помощью интерпретатора .Bourne shell (независимо от того, какой интерпретатор используется в интерактивном ,режиме, поскольку большинство сценариев данной книги рассчитаны на Bourne shell). : Чтобы проверить свою систему, предлагаем создать файл testing, содержащий две строки. Примечание: Не создавайте программ с именем test. Существует важная системная команда test (44.20), и ваша программа случайно может быть использована вместо системной команды. Присваивайте тестовым программам какие угодно имена (testing, atest и т.д.), но только не test. В параграфе 44.21 показано, как выбрать уникальное имя файла. 1. Создайте файл с именем testing (с помощью текстового редактора или команды cat > testing (25.02)). Поместите в этот файл следующие две строки (убедитесь, что ввод начат с первой строки файла и что символ # находится в первой позиции первой строки): #!'/bin/echo Just export'stuff 2. Выйдите из редактора, сохранив файл. Сделайте файл исполняемым посредством команды chmod +x testing (44.02). Теперь запустите программу, набрав ее имя по приглашению интерпретатора shell. Возможны четыре вида откликов. 1. Если полученный вами результат, совпадает с тем, что приведен ниже, запись f! работает. Вы сможете сообщать своей системе, какой интерпретатор shell должен-выполнять тот или иной сценарий: % testing just testing * На самом деле запись #! можно использовать для задания любой интерпретирующей программы w.tv). а не только интерпретатора shell. Программирование для начинающих пз
44.05 Ответ just ./testing также свидетельствует о том, что запись #! работает. Сообщение об ошибке типа testing: command not found может указывать, что ваш текущий каталог отсутствует в последовательности поиска (8.07). В этом случае попробуйте выполнить команду ./testing. 2. Результат % testing % является признаком того, что ваша система не понимает запись #!, но каким-то образом ей удалось выполнить программу с помощью интерпретатора Bourne shell. 3. Если вы получили строки % testing #!: not found % значит, ваша система запускает программу с помощью старой версии интерпретатора Bourne shell. Вы не должны использовать строки комментария, начинающиеся с символа #. 4. В следующем случае ваша система не понимает запись #! и запускает программу посредством интерпретатора С shell: % testing export: Command not found \ Многие UNIX-системы, особенно новые, дают ответ just testing или just ./testing. Это происходит потому, что, как указано в параграфе 45.05, система удаляет #! в начале строки и добавляет имя файла сценария (или его путевое имя) в конец строки, после чего запускает этот файл: /bin/echo just testing (Команда echo описана в параграфе 8.06.) Если ваша система выполнила сценарий с помощью интерпретатора С shell, найдите способ, обеспечивающий использование Bourne shell. Попробуйте реализовать трюк из параграфа 45.06 или обратитесь за консультацией к системному администратору. - JP 44.05 Проверка условий с помощью оператора case интерпретатора Bourne shell При вводе команды в командной строке вы можете наблюдать, что происходит, и решать, какую команду выполнять дальше. Сценарий shell принимает такие решения автоматически. Для этого служит оператор case. Этот оператор сравнивает строку (которая обычно является значением переменной интерпретатора shell или переменной среды (6.os, e.oi)) с одним или несколькими шаблонами. Шаблоном может быть обыкновенная строка (слово, цифра и т.п.) или выражение с метасимволами оператора case (м.щ. Когда оператор case обнаруживает совпадение шаблона и строки, он выполняет одну или несколько команд. Приведем пример. В нем проверяется значение переменной среды TERM(s.iO). При использовании терминала VT100 или 1X4023 на него посылаются некоторые символы. Если же работа ведется на другом терминале, выводится сообщение об ошибке и программа завершается: case "$TERM" in echo...\027' 4S.3S vtlOO) echo 'ea[w' I tr 'eaw' • \033\001\027' ;; tk4023) echo "*[p23" ;; *) # He VT100 или ТК4023. Вывод сообщения об ошибке: 1>&2S.06 echo "progname: quitting: you aren't on VT100 or TK4023." 1>&2 exit 44.11 exit 714 Часть восьмая. Программирование в среде shell
44.07 Рассмотрим подробнее, как все происходит. Оператор case сравнивает выражение, расположенное между словами case и in, с выражениями, которые находятся у левого края строк и заканчиваются правой скобкой. Если выражение совпадает с первым шаблоном (в данном примере — vtlOO), выполняются команды до ;;. Символы ;; служат указанием перейти к оператору esac (esac — это слово case, записанное наоборот). Перед ;; можно вставить сколько угодно команд, но при этом следует либо разместить каждую команду в отдельной строке, либо разделить команды точками с запятой (s.os)). Если первый шаблон не подходит, интерпретатор shell проверяет следующий (tk4023). Как и раньше, при совпадении выполняются последующие команды и происходит переход к оператору esac. А если нет совпадения? Следующим шаблоном является метасимвол *. Он соответствует любому условию, отличному от vtlOO или tk4023 (например, xterm или пустой строке). Можно использовать любое количество шаблонов. Выполняться будут команды, которые следуют за первым совпавшим шаблоном. Ничего страшного, если ни один из шаблонов не совпадет. - JP 44.06 Шаблоны оператора case Оператор case (44.05) удобен для сравнения строк. Метасимволы, используемые в его шаблонах, имеют те же функции, что и метасимволы в именах файлов о.щ интерпретатора shell, но с некоторыми особенностями. Вот несколько примеров. ?!) Соответствует строке, состоящей из одного символа, например а, 3, ! и т.д. ?*) Соответствует строке, содержащей один или несколько символов (не пустой строке). [уУ] I [уУ] [еЕ] [sS]) Соответствует символам у, У или словам yes, Yes и т.д. Символ | означает "или". /*/* [0-9]) Соответствует такому имени файла, как /xxx/yyy/somedir/file2, которое начинается с косой черты, содержит по крайней мере еще одну косую черту и заканчивается цифрой. 'What now?') Соответствует шаблону What now?. Кавычки (s.u> сообщают интерпретатору shell, что строку нужно рассматривать буквально: не разбивать ее в позициях пробелов и не считать ? метасимволом. "$msgs") Соответствует содержимому переменной msgs. Двойные кавычки позволяют интерпретатору shell подставлять значение переменной. Они также защищают пробелы и другие специальные символы от интерпретации. Например, если переменная msgs содержит first next, шаблон соответствует целой строке first next. - JP 44.07 Код завершения процесса Когда процесс (команда) завершается, он может возвратить числовой код своего состояния процессу, который его вызвал (запустил). По коду состояния вызывающий процесс определяет, как завершилось выполнение команды — успешно или нет. Многие команды UNIX возвращают код 0 при положительном исходе и код, отличный от 0 (1, 2 и т.д.), если что-то было выполнено неправильно. Некоторые команды, например grep и diff, для каждой ошибки выдают особый ненулевой код (конкретные значения приведены в документации). Интерпретатор Bourne shell помещает код завершения предыдущей команды в переменную ?. Ее значение можно получить, поставив перед именем знак доллара, как и в случае любой другой переменной. Например, копируя файл, команда ср устанавливает код завершения 0. Если что-то происходит неправильно, она возвращает 1: $ ср afile /tmp $ echo $? Программирование для начинающих 715
44.08 (0) false О S cp afilel, /tmp cp;.afilel: Not such file or directory $ echo $? 1 В интерпретаторе С shell для этого используется переменная status: % cp afilel /top cp: afilel: Not such file or directory % echo $status 1 Как правило, код завершения не нужно выводить на экран. Существует несколько способов (44.08,44.09,44.10) использования кода завершения одной команды как условия выполнения следующей. Имеются две простые утилиты UNIX, которые ничего не делают кроме того, что возвращают код завершения. Утилита true возвращает 0 (нуль), a false — I (единицу); Они имеют GNU-версии, записанные на компакт-диске, но, увы, без каких-либо дополнительных воз- h^| можностей. ;-) Код завершения конвейера а.М) соответствует состоянию последней команды в нем.* В интерпретаторе Bourne shell невозможно проверить код завершения фонового задания, если не использовать команду wait, с помощью которой отслеживается момент завершения задания (по сути, эта команда переводит задание в интерактивный режим). - JP 44.08 Проверка кода завершения с помощью оператора if Для написания сценариев любой степени сложности необходимо уметь создавать условные выражения. Такие выражения возвращают значение "истина" или "ложь". Например: "Я уже позавтракал?", "Сейчас больше 5 часов?", "Существует ли файл indatdl", "Значение переменной $aardvark больше 60?". Shell является полноценным языком программирования. Следовательно, как и языки С, Basic, . Pascal и т.д., он.позволяет.включать в сценарии операторы if с условными выражениями. Условные выражения можно использовать в нескольких ситуациях, но чаще всего они применяются в операторе if. Ниже показан синтаксис конструкции jf для интерпретатора Bourne shell: ' " if' условие ' then # выполнить, если условие возвращает код нуль ("истина") одна_нли_не сколько_коыа нд else # выпрлнить, если условие возвращает код не нуль ("ложь") одна_или_несколько_команд f i" ' Можно опустить оператор else и следующий- за ним.блок кода. Однако нельзя опускать операторы then и Д Если вы хотите выполнить какое-то действие, когда условие ложно, и не делать ничего, когда оно истинно, видоизмените конструкцию следующим образом: if условие then : # не делать ничего else # выполнить, если условие возвращает код не нуль ("ложь") одна_или_не сколько_кома нд fi . . В первых версиях интерпретатора Кош shell и, как мне кажется, где-то еще (это было очень давно) я несколько раз сталкивался с тем, что все происходит не так. 716 Часть восьмая. Программирование в среде shell
44.09 Обратите внимание: в этой конструкции использована специальная пустая команда двоеточие (:) (45.09). Есть еще один, более удобный, способ инвертирования смысла условия (выполнения действия, когда условие ложно) — применение оператора I I (44.вя) (две вертикальные черты). Не забудьте в конце конструкции поместить оператор fi. Его отсутствие — на удивление распространенный источник ошибок (по крайней мере, у меня). Другая распространенная ошибка состоит в следующем. Как следует из руководства, допускается применение в одной строке операторов if then и else. Да, действительно, однако это не всегда просто. Рекомендуем вам поступать следующим образом: записывать конструкции // так, как показано выше. В результате вы избежите многих проблем и даже сможете с первого раза правильно написать программу. Вот пример из реальной жизни — сценарий bkedit, создающий резервную копию файла перед его редактированием. Если команда ср возвращает нулевой код, сценарий редактирует файл, иначе он выводит сообщение. (Выражение $1 заменяется первым аргументом командной строки; см. параграф 44.15.) #!/bin/sh if ср "$1" "$1.Ьа1сп then vi "$1" else \>&18.06 echo "bkedit quitting: can't make backup?" 1>S2 fi Попробуйте набрать и запустить этот сценарий. Или же просто введите строки (начиная с if) на терминале. При этом вместо $1 используйте настоящее имя файла. Конструкцию // часто сочетают с командой test (44.20), которая выполняет проверку и возвращает код 0 или 1. - ML, JP 44.09 Проверка успешности выполнения команды Интерпретаторы shell позволяют прямо в командной строке проверять, успешно ли выполнена команда. Такая возможность обеспечивает удобный способ написания кратких и понятных сценариев. Я имею в виду операторы II и ss (в первую очередь — оператор | I). Выражение команда 1 | | команда2 обычно объясняют так: "Выполнить команду справа, если команда слева не выполнена успешно". Я предпочитаю трактовать его как конструкцию "одно из": "Выполнить либо команду]., либо команду2". Проиллюстрируем сказанное примером: cat filea fileb > filec I| exit Это значит, что нужно либо вывести содержимое файлов {cat), либо выйти (exit). Если содержимое файлов вывести невозможно (команда cat возвращает код 1), выполняется команда exit (38.04). Если же команда cat срабатывает успешно, выход не производится, т.е. выполняется либо левая часть выражения, либо правая. Перед тем как выйти, мы можем вывести сообщение об ошибке: cat filea fileb > filec I| { I>42 SM echo sorry, no dice I>s2 exit 1 ) Аналогично: выражение команда1 ss команда2 означает "выполнить команду1 И коман- ду2" или "выполнить команду2, если команда1 выполнена успешно". (Но если первую команду выполнить невозможно, не должна быть выполнена ни одна команда.) Это полезно, например, при необходимости напечатать временный файл и сразу же удалить его. "' lpr file SS rm file Программирование для начинающих 717
44.10 catsaway Если по какой-то причине команда Ipr не выполняется, файл нужно сохранить. Опять-таки, я хочу подчеркнуть, как все это следует понимать: напечатать файл и удалить его. (Подразумевается, что если файл не распечатан, то он не удаляется.) - ML 44.10 Циклы, в которых проверяется код завершения В интерпретаторе Bourne shell есть два вида циклов, в которых запускается команда и проверяется ее код завершения. Цикл until продолжается, пока команда не возвратит нулевой код, а цикл while — пока команда возвращает нулевой код. Повторение цикпа до успешного выполнения команды Цикл until повторно запускает команду, пока она не будет выполнена успешно. Это значит, что если команда возвращает ненулевой код, интерпретатор shell снова выполняет тело цикла и проверяет условие завершения цикла. Shell продолжает выполнять команду, пока она не вернет нулевой код, как показано в следующем примере: % cat sysmgr #!/bin/sh until who I grep "ЛЬагЬ " do sleep 60 done echo The system manager just logged on. % sysmgr & [1] 2345 ...спустя некоторое время... barb ttyp7 Jul 15 09:30 The system manager just logged on. В цикле запускается команда who (Si.04), вывод которой по каналу передается команде grep (27.01). Команда grep производит поиск любой строки, начинающейся со слова barb и пробела. (Пробел позволяет избежать отбора таких имен, как barbara.) Если команда grep возвращает не нуль (подходящих строк нет), интерпретатор shell ожидает 60 секунд. Затем цикл повторяется и сценарий снова пытается выполнить команды who | grep. Он делает это до тех пор, пока команда grep не вернет нулевой код. Тогда цикл завершается и управление передается строке после оператора done. Команда echo выводит сообщение, после чего сценарий завершается. (Я выполняю этот сценарий в фоновом режиме (1.26). Это позволяет мне решать другие задачи до обнаружения пользователя barb.) [Цикл until в интерпретаторе Bourne shell отличается от конструкций until в большинстве языков программирования тем, что в его случае условие проверяется в начале цикла. Почти во всех языках условие в цикле until проверяется в конце цикла. — ML] Повторение цикпа, пока команда выполняется успешно Цикл while противоположен циклу until. В нем происходит повторное выполнение команды, пока она завершается успешно (возвращает нулевой код). В программе catsaway (см. ниже) используется цикл while для наблюдения за выводом команды who, пока системный администратор не выйдет ® 1 из системы. Программа catsaway является противоположностью сценарию sysmgr. % cat catsaway #!/bin/sh /dev/null 13.14 awhile who I grep "Abarb " > /dev/null do sleep 60 done ■ echo "The cat's away..." % catsaway & [1] 4567 ...спустя некоторое время... The cat's away... - JP 718 Часть восьмая. Программирование в среде shell
44.12 44.11 Установка кода завершения сценария Большинство стандартных команд UNIX возвращают код завершения (+$.07). Ваш сценарий также должен делать это. В данном параграфе показано, как установить правильный код завершения для нормального и для ошибочного выхода. Для завершения сценария, а также для установки его кода завершения предназначена команда exit. Передайте этой команде код завершения вашего сценария. Если он не имеет определенного кода завершения, будет возвращаться код завершения последней выполненной команды. Рассмотрим пример — переписанный сценарий bkedit из параграфа 44.08. Если сценарий может создать резервную копию, запускается редактор и сценарий возвращает код завершения редактора vi (обычно 0). Если с копией что-то не ладится, сценарий выводит сообщение об ошибке и возвращает код I. Вот текст сценария: #!/bin/sh if ср "$1" "$l.bak" then vi "$1" exit # ИСПОЛЬЗОВАНИЕ КОДА ВОЗВРАТА КОМАНДЫ vi else echo "bkedit quitting: can't make backup?" 1>&2 exit 1 fi При запуске сценария без имени файла происходит следующее: $ bkedit ср: usage: ср fnl fn2 or cp fnl [fn2...] dir bkedit quitting: can't make backup? А вот что останется в переменной, где хранится код завершения: $ echo $? 1 - JP 44.12 Обработка сигналов прерывания команд Если в процессе выполнения сценария нажать клавишу прерывания (5.щ (например, [CTRL-c]), произойдет немедленный выход из интерпретатора shell. Это может вызвать проблемы, если в сценарии используются временные файлы, поскольку при внезапном выходе существует вероятность, что они останутся неудаленными. Команда trap позволяет сообщить интерпретатору shell, что он должен сделать, прежде чем завершить работу (см. табл. 44.1). Ознакомьтесь со сценарием zpg, который использует временный файл /tmp/zpg$$, находящийся в системном каталоге временных файлов (2Ш). Интерпретатор shell заменяет $$ своим идентификатором процесса (зш). Поскольку никакой другой процесс не может иметь тот же идентификатор, данный файл получит уникальное имя. Сценарий распаковывает (24.07) файл, указанный в его командной строке, а затем запускает программу просмотра файлов pg* В сценарии применяется команда trap, а временные файлы удаляются, даже есл1т пользователь нажмет [CTRL-c]. Кроме того, сценарий устанавливает код завершения, равный 1, который сбрасывается в 0, если команда pg завершается сама по себе (без прерывания). #!/bin/sh # zpg - РАСПАКОВКА ФАЙЛА И ЕГО ПРОСМОТР С ПОМОЩЬЮ КОМАНДЫ pg # Использование: zpg имя_файла * Можно было бы непосредственно из сценария запустить команды gzcat $1 I pg, но некоторые версии команды pg не создают резервную копию при чтении из канала. bkedit 1>&2 S.06 Программирование для начинающих 719
stat=l # ИСХОДНЫЙ КОД ЗАВЕРШЕНИЯ; СБРАСЫВАЕТСЯ В О # ПЕРЕД НОРМАЛЬНЫМ ЗАВЕРШЕНИЕМ temp=/tmp/zpg$$ trap 'rm -f $temp; exit $stat' 0 trap 'echo "'basename $0~: Ouch! Quitting early." 1>S2' 1.2 15 case $# in 1) gzcat "$1" >$temp pg $temp stat=0 *) echo "Usage: "basename $0" filename" 1>S2 ;; esac Сценарий содержит две команды trap. • Первая команда trap, с числом 0 в конце, выполняется при каждом выходе из интерпретатора shell — как при нормальном выходе, так и в случае прерывания. При этом выполняются команды, заключенные в одинарные кавычки. В данном примере есть две команды, разделенные точками с запятой (s.os). Первая команда предназначена для удаления временного файла (в данном случае благодаря опции -/ (шв) команда гт не выводит сообщения об ошибке, если файла пока не существует). Вторая команда служит для завершения сценария с кодом возврата, сохраненным в переменной stat. Просмотрите дальнейший текст сценария: значение $stat всегда будет равно 1, если только команда pg не завершится самостоятельно. В этом случае переменная stat будет установлена в 0. Следовательно, данный сценарий всегда возвращает правильный код завершения: если перед завершением сценарий был прерван, он возвращает 1, иначе — 0.* • Вторая команда trap завершается цифрами 12 15. Это номера сигналов, соответствующих различным видам прерываний. В новых версиях интерпретатора shell вместо номеров можно использовать имена прерываний. Их краткий список приведен в табл. 44.1. Чтобы получить список всех сигналов, введите kill -1 (строчная L) или обратитесь к интерактивному руководству для функции signalQ). Вторая команда trap выполняется при ненормальном выходе (например, по сигналу [CTRL-c]). При этом выводится сообщение, вместо которого может быть выполнена любая другая команда. Таблица 44.1. Номера сигналов UNIX для команды trap Номер сигнала 0 1 2-w 3 15 Имя сигнала EXIT HUP INT QUIT TERM Пояснения Команда exit Сигнализирует о завершении рабочей сессии Прерывание (часто — [CTRL-c]) Выход (часто - [CTRL-\]) Сигнал от команды kill Сценарии не всегда содержат две команды trap. В качестве примера предлагаем вам сценарий пот as. 09). В своих сценариях я обычно не обрабатываю сигнал 3 (QUIT). Это обеспечивает возможность прерывания сценария без удаления временных файлов и выполнения других завершающих действий. В сценариях общего назначения я, как правило, фиксирую такие сигналы. Обратите внимание на то, что команды echo в данном сценарии имеют в конце оператор 1>&2 (45.ii). Он сообщает интерпретатору Bourne shell, что вывод команды echo следует помещать в стандартный поток ошибок, а не в стандартный вывод. Это удобный прием, благодаря которому сообщения об ошибках выводятся на экран, а не направляются в файл или по каналу вместе с нормальным выходным текстом. (В данном сценарии это не имеет * В команде trap необходимо использовать одинарные, а не двойные кавычки: Тогда значение^ $sta t не интерпретируется до тех пор, пока при Выходе из сценария не будет выполнена команда Imp. 720 Часть восьмая. Программирование в среде shell 44.12 exit 44.11
/ 44.14 большого значения, поскольку он выполняется в интерактивном режиме. Но лучше придерживаться этого правила во всех сценариях.) Если ваша команда trap запускает последовательность команд, то, возможно, удобнее вызвать в команде функцию ро.ю), а не указывать список команд: trap имя_функции 1 2 15 - JP 44.13 Команда read: чтение данных с клавиатуры Команда read интерпретатора Bourne shell читает строку из одного или нескольких слов, полученных с клавиатуры (или со стандартного ввода),* и сохраняет прочитанные слова в одной или нескольких переменных интерпретатора shell. Эта команда обычно используется для чтения ответа с клавиатуры. Например: echo ,-п "Type the filename:" read filename • Если указать имя одной переменной, команда read сохранит в ней всю строку: read имя_переменной • При указании нескольких переменных первое введенное слово попадет в первую переменную, второе — во вторую и т.д. Остальные слова помещаются в последнюю переменную. Например, мы ввели следующие команды: echo -n "Enter first and last name: " read fn In Если теперь пользователь наберет John Smith, слово John попадет в переменную fit, a слово Smith — в переменную In. Если же ввести Jane de Boes, в переменную Jh будет помещено слово Jane, а в переменную In — слова de Boes. Некоторые версии интерпретатора Bourne shell поддерживают встроенную функцию line, которая читает стандартный ввод и направляет прочитанные значения на стандартный вывод. Данная функция применяется следующим образом: переменная='line" Утилита grabchars (4S.32) позволяет читать данные с клавиатуры, не нажимая клавишу [RETURN]. - JP 44.14 Включение в сценарий команд awk, sed и др. В своей статье о программе awk, опубликованной в журнале SunExpert (январь 1991 г.), Питер Коллинсон (Peter Collinson) предлагает использовать эту программу в сценариях shell (44.ni) следующим образом: #!/bin/sh awkprog=' /footprint $3} /bar/{print $4}' awk "awkprog" $* С его точки зрения, такая конструкция более понятна, поскольку программа и команда разделены. Например: grep foo $input I sed .... I awk "$awkprog" - I ... * Некоторые ранние версии команды read не работали с оператором переадресации < оз.оп, оии могут читать данные только с терминала. Программирование для начинающих 721
44.15 Не каждый будет в восторге от "преимуществ" записи команды awk таким способом, однако стандартный способ записи, действительно, имеет недостатки. Вот еще более сложный вариант: #!/bin/sh temp=/tmp/awk.prog.$ $ <<\3.1Я cat >$temp «\END /footprint $3} /bar/{pcint $4} END awk -f $temp $1 rm -f $temp Данный вариант делает более простым динамическое создание сложных программ. Последняя команда (awk) становится эквивалентом команды eval (s.io/, она выполняет то, что быдо встроено в программу во время ее выполнения. Применяя первый подход (программу, содержащуюся в переменной интерпретатора shell), также можно обеспечить выполнение работы таким способом. / Приведу еще один пример. Как-то мне пришлось использовать длинный конвейер, описанный в 200 строках. Гигантские awk- и ierf-сценарии были вставлены посредине. Bj результате программа оказалась совершенно непонятной. Но если каждую программу начинать блоком комментария и заканчивать символом канала, результат будет удобочитаемым. Это более рациональный подход, чем применение переменных или временных файлов. Он эффективен при использовании нескольких сценариев. # # ЧТЕНИЕ ФАЙЛА И ВЫПОЛНЕНИЕ XXX С ПОМОЩЬЮ awk: # awk ' ... awk-программа, записанная с отступом. .. # # СОРТИРОВКА ПО ПЕРВОМУ ПОЛЮ, ЗАТЕМ ПО XXX: # sort +0n -1 +3г | # # ОБРАБОТКА СТРОК С ПОМОЩЬЮ sed И XXX: # sed ' Многострочные конвейеры, подобные приведенному, в интерпретаторе С shell выглядят менее привлекательно, поскольку каждая строка заканчивается обратной косой чертой (s.is). В параграфах 8.14 и 8.15 более подробно рассказывается о защите специальных символов от интерпретации. - ML, JP 44.15 Обработка аргументов командной строки в сценариях Чтобы написать гибкий сценарий, необходимо предусмотреть возможность передачи ему аргументов командной строки. Как мы уже отмечали в других параграфах (44. и, 44.12), значением переменной $ 1 является первый аргумент командой строки. Интерпретатор Bourne shell может предоставить до девяти аргументов ($9). Интерпретатор Korn shell и некоторые новые интерпретаторы shell понимают запись ${10} для десятого аргумента и т.д. (Параграф 8.05 содержит обзор способов обработки командной строки интерпретатором shell.) 722 Часть восьмая. Программирование в среда shell
44.16 С помощью параметра "$@" Если вы прочитали эту серию (44.oi) парафафов по порядку, то обратили внимание на сценарий гря (44.12), который принимает всего один аргумент командой строки. Если поместить в сценарий параметр "$@", shell заменит его набором заключенных в кавычки (s.u) аргументов командной строки. В таком случае можно передавать какие угодно аргументы, в том числе путевые имена с необычными символами (23.иу. % zpg report memo "eavearts/What's next?" Третий аргумент содержит вполне допустимое имя файла. Такие имена все чаще встречаются в наших системах, особенно в файловых системах, смонтированных через сеть из таких компьютеров, как Macintosh, где пробелы и другие специальные символы в именах файлов — обычное явление. Двойные кавычки являются гарантией того, что сценарий сможет работать с этими необычными (но допустимыми!) именами файлов. В подобном случае необходимо обработать аргументы посредством команды gzcat. Изменим сценарий zpg следующим образом: gzcat "$@" >$temp Когда интерпретатор shell приступит к выполнению этого сценария с аргументами, приведенными выше, командная строка приобретет такой вид: gzcat "report" "memo" "savearts/What's next?" >/tmp/zpgl2345 Примечание: В некоторых версиях интерпретатора Bourne shell при отсутствии аргументов командной строки "$@" превращается в единственный пустой аргумент (4б.о?), как если бы вы ввели следующее: gzcat "" >/tmp/zpgl2345 В этом случае команда gzcat выдает сообщения о том, что не может найти файл. (Конечно, этой проблемы можно избежать с помощью оператора case. Однако не все сценарии проверяют количество аргументов.) В таких версиях выражение "$@" можно заменить выражением $(1 + "$@"} (45.12). Это означает, что если определен первый аргумент ($1), то должен использоваться параметр "$@". Несколько худшим решением является подстановка $* вместо "$@". При этом формируется список аргументов командной строки, однако эти аргументы не заключаются в кавычки. Иногда подобное может вызвать проблемы, связанные с путевыми именами, которые содержат специальные символы. С помощью циклической конструкции Цикл for (44.I6) обрабатывает все аргументы по очереди. Можно также использовать цикл while (44.W), который проверяет параметр $fr (см. ниже) и один за другим удаляет аргументы посредством команды shift (44.17). Команды getopt и getopts (44.IS) обрабатывают аргументы стандартным способом. Подсчет аргументов с помощью параметра $# Значением параметра $# является количество аргументов в командной строке. Например, если их три, то $fr будет содержать 3. Обычно значение этой переменной используется для обнаружения ошибок (как в сценарии zpg", см. параграф 44.12) с помощью оператора case (uos) или команды test (44.20). - JP 44.16 Обработка аргументов командной строки с помощью цикла for Иногда необходим сценарий, который по очереди обрабатывает аргументы командной строки. (Параметр "$@" (44.15) передает для обработки сразу все аргументы.) Цикл for интерпретатора Программирование для начинающих 723
44.16 Boume shell позволит организовать поочередную обработку аргументов. Синтаксис конструкции for следующий: for arg in список do ...обработка $arg... done Если опустить фразу in список, цикл будет обрабатывать аргументы командной строки по очереди. В этом случае он помещает первый аргумент в переменную arg (или в переменную интерпретатора shell (6.os) с любым другим именем), а затем выполняет команды между операторами do и done. Далее он помещает в переменную arg следующий аргумент, выполняет команды цикла и т.д. После обработки всех аргументов цикл, завершается. Чтобы испытать на практике действие цикла for, поэкспериментируем со сценарием ^gg (44.12). ff!/bin/sh # zpg - РАСПАКОВЫВАНИЕ ФАЙЛА И ЕГО ПРОСМОТР С ПОМОЩЬЮ КОМАНДЫ рд # Использование: zpg [опции команды рд] имя_файла [... файлы]. Stat=l # КОД ЗАВЕРШЕНИЯ; СБРАСЫВАЕТСЯ ВО # ПЕРЕД НОРМАЛЬНЫМ ЗАВЕРШЕНИЕМ temp=/tmp/zpg$$ trap 'rm -f $temp; exit $stat' 0 trap 'echo "'basename $0': Ouch! Quitting early..." 1>&2' 1 2 15 files= switches3* for arg do case 44.06 case "$arg" in -*) switches="$switches $arg" ;; *) files="$files $arg" ;; esac done case "$files" in "") echo "Usage: 'basename SO' tpg options] file [files]" I>s2 ;; *) for file in $files do gzcat "$file" I pg $switches done stat=0 r / esac Цикл for добавлен для получения и проверки каждого аргумента командной строки. Предположим, пользователь ввел: % zpg -n afile ../bfile На первом проходе цикла for переменная $arg равна -п. Поскольку аргумент"начинается со знака "минус", оператор case рассматривает его как опцию. В этом случае значение переменной $switches заменяется ее предыдущим значением (пустой строкой), пробелом и -п. Управление передается оператору esac, и цикл повторяется для обработки следующего аргумента. Следующий аргумент — af ile — не является опцией. Поэтому переменная $files будет содержать пробел и afile. Цикл запускается еще раз, при этом значением переменной $arg является .. /bfile. Такое значение выглядит как имя файла, поэтому переменной $files присваивается значение afile ../bfile. Поскольку ../bfile — последний аргумент, цикл завершается. Переменная $switches содержит опции, a $files — остальные аргументы. Далее мы добавили еще один цикл ./or. Оператор цикла содержит выражение in $f iles, поэтому цикл просматривает содержимое переменной $ files. К каждому файлу применяется команда gzpat, вывод которой передается вместе со всеми введенными вами огщиями-каманде pg. т Часть восьмая. Программирование в средв shell
44.17 Заметим, что параметр $ switches не заключен в кавычки (s.u). Таким образом, если переменная $switches пуста, интерпретатор shell не будет передавать пустой аргумент команде pg. Кроме того, если переменная $switches содержит более одной опции, shell разобьет их на отдельные аргументы и передаст команде pg. В цикле for можно использовать не только имена файлов, но и любой список слов, элементы которого, как правило, разделены пробелами (на самом деле — разделителями, указанными в переменной IFS (35Л)). Для хранения списка не обязательно применять переменную интерпретатора shell. В этих целях можно подставлять результаты выполнения команды <9.i«) (обратные кавычки), применять метасимволы интерпретатора shell (15.02) и даже жестко заданный список слов: for person in Joe Leslie Edie Allan do echo "Dear Sperson," I cat - form_letter Ilpr done Кроме того, для обработки аргументов командной строки можно использовать команды getopt И getoptS (44.18). - JP 44.17 Обработка аргументов командной строки с помощью цикла while и команды shift Цикл for великолепен, если все аргументы командной строки нужно обработать по очереди. Но, как это часто бывает, некоторые аргументы являются опциями, имеющими собственные аргументы. Например, в командной строке grep -f имя_файла параметр имя_файла представляет собой аргумент опции -f. Опция и аргумент должны обрабатываться вместе. В подобных случаях удобным способом обработки является сочетание операторов и команд while (44.Ю), test (44.20), case (44.05) и shift. Вот пример конструкции: while [ $# -gt 0 ] do case "$1"- in -a) options="$options $1";; -f) options="$options $1" argfile="$2" shift *). files="$files $1";; esac shift done Фокус заключается в следующем: команда shift удаляет аргумент из списка аргументов сценария, в котором происходит сдвиг на одну позицию ($1 пропадает, $2 становится $1, $3 — $2 и т.д.) Для совместной обработки опции и ее аргумента выполняется еще один сдвиг (shift). В цикле while используется команда test (44.20), которая следит за тем, чтобы значение $fr (количество , аргументов) было больше нуля. Работа продолжается, пока это выражение не станет ложным; данное событие произойдет, когда все аргументы будут исчерпаны. Между прочим, все, что должна делать конструкция case, — поочередно сравнивать значение переменной $1 со строками опций. В простом примере, приведенном выше, мы исходили из того; что все элементы, начинающиеся со знака "минус", являются опциями, которые мы хотим передать некоторой программе, вызываемой сценарием. Поэтому мы просто создаем переменную интерпретатора shell, в которую последовательно будут занесены все опции. Вероятно, можно выбрать иную тактику: использовать другие переменные интерпретатора shell и выполняемые команды. - 13.13 lpr 43.02 Программирование для начинающих 72$
44.18 Будем считать, что элементы, которые не содержат знак "минус", являются именами файлов. В этом случае обработку можно выполнить более надежно, используя команду test для проверки того, что аргумент представляет собой имя файла. Приведем пример простого сценария, в котором рассмотренная конструкция применяется для передачи имен файлов команде рг, а из нее — программе преобразования текста в формат PostScript и программе вывода на печать: while [ $# -пе 0 ] do case $1 in +*) pages="$l" ;; *) if [ -f "$1" ]; then files=**$files $1" else echo "$0: file $1 not found" 1>&2 fi;; esac shift done pc $pages $files | psprint I lpc Такой подход, возможно, устарел, поскольку в некоторых системах употребляется команда getopts (м. is), которая позволяет распознавать в командной строке опции наподобие -abc и менять их на -а -Ь -с. Однако я все еще считаю его удобным. [Опция +список_страниц команды рг начинается со знака "плюс". Команды getopt и getopts не поддерживают таких устаревших опций. — JP\ - TOR 44.18 Стандартный анализ командной строки Большинство сценариев должны обрабатывать аргументы командной строки — опции, имена файлов и т.д. В параграфах 44.15, 44.16 и 44.17 показано, как анализировать командную строку в интерпретаторе Bourne shell. Описанные способы имеют два недостатка. Во-первых, нельзя комбинировать аргументы с помощью единственного дефиса (например, -abc вместо -а -Ь -с). Во-вторых, невозможно задать аргументы опций без пробела между ними (например, -Ъаргумент вместо -Ь аргумент)* Ваш интерпретатор Bourne shell может поддерживать встроенную команду getopts." Эта команда позволяет работать с многочисленными сложными опциями без упомянутых выше ограничений. Чтобы определить, имеет ли интерпретатор shell команду getopts, ознакомьтесь со страницей интерактивного руководства sh или getopts(\). НЕсли ваш интерпретатор shell не поддерживает команду getopts, можно воспользоваться командой getopt, имеющейся на компакт-диске. Она вызывается иначе, чем getopts, но здесь мы не будем касаться этого. getopt Команда getopt принимает два аргумента и более. Первый из них является строкой, содержащей буквы и двоеточия. Каждая буква соответствует некоторой допустимой опции. Если за буквой следует двоеточие, значит, данная опция требует аргумента. Второй и следующие аргументы представляют собой опции введенной командной строки. Обычно для передачи аргументов команде getopt применяется параметр "$@" (44.15). * Хотя большинство команд UNIX допускают такой способ записи, он противоречит общепринятым синтаксическим правилам, которые изложены во введении к руководству пользователя. Версия команды getopt, содержащаяся иа компакт-диске, поддерживает описанный выше синтаксис. Те команды getopt, которые нам встречались, также поддерживают его, но в дальнейшем могут лишиться этой черты. ** Как интерпретатор bash, так и ksli поддерживают эту команду. Она послужила заменой старой команде getopt. Команда getopts больше соответствует синтаксису языка shell и работает эффективнее. Программисты на С должны заметить, что эта команда очень похожа на стандартную библиотечную подпрограмму getopt(3). 726 Часть восьмая. Программирование в среде shell
44.18 II 44.09 Команда getopt извлекает из командной строки каждую опцию, проверяет ее допустимость и записывает правильную опцию в стандартный вывод. Если опция имеет аргумент, команда getopt записывает этот аргумент после опции. Когда команда getopt обнаруживает аргумент, не являющийся опцией (первый аргумент, который не начинается с дефиса), она выводит два дефиса и остальные аргументы. При обнаружении неправильной опции или опции, которая должна иметь аргумент, но не имеет его, выводится сообщение об ошибке и возвращается ненулевой код завершения (44.07). В вашем сценарии может использоваться цикл для анализа вывода команды getopt. Вот пример aptSt""* сценария opttest, демонстрирующий работу команды getopt. #!/bin/sh set — 'getopt "ab:" "$@"'- || ( \Уз~Ю echo "Usage: 'basename $0" [-a] [-b name] [files]" 1>S2 exit 1 } echo "Before loop, command line has: $*" aflag=0 name=NONE ■45.09 while : do case "$1" in -a) aflag=l ;; -b) shift; name="$l" ;; —) break ;; esac shift done shift ff УДАЛЕНИЕ ЗАВЕРШАЮЩИХ — echo "aflag=$aflag / name=$name /Files are $*" Этот сценарий имеет две допустимые опции. Опция -а обеспечивает установку переменной ajlag равной 1. Опция -Ь имеет единственный аргумент, который сохраняется в переменной пате. Остальные аргументы представляют собой имена файлов. Выполнение сценария начинается с запуска команды getopt (в обратных кавычках (9.mj) и активизации команды set (44.I9), заменяющей аргумент командной строки выводом команды getopt. Большое значение имеет первый аргумент команды set — — (два дефиса) (44.19/. он гарантирует, что команда set передаст опции сценария команде getopt, а не будет рассматривать их как опции интерпретатора shell. Команда echo показывает вывод команды getopt, который впоследствии анализируется в цикле, где при этом устанавливаются переменные интерпретатора shell. Когда в цикле обнаруживается аргумент --, поступивший от команды getopt, цикл прерывается и оставляет в командной строке имена файлов (если таковые имеются). Вторая команда echo показывает содержимое переменных интерпретатора shell и командной строки после завершения цикла. Вот несколько примеров: % opttest Before loop, command line has: — aflag=0 / name=NONE /Files are % opttest -b filel -a file2 file3 Before loop, command line has: -b filel -a — file2 file3 aflag=l / name=filel /Files are file2 file3 % opttest -q -b filel getopt: illegal option — q Osage: opttest [-a] [-b name] [files] % opttest -bfilel Before loop, command line has: -b filel -- aflag=0 / name=filel /Files are % opttest -ab getopt: option requires an argument — b Usage: opttest [-a] [-b name] [files] Программирование для начинающих 727
44.19 Преимущество" команды getopi состоит в том, что она минимизирует дополнительный код, необходимый для обработки опций, и полностью поддерживает стандартный синтаксис UNIX для опций (как описано в документации). -JP.BR 44.19 Команда set интерпретатора Bourne shell [Материал этого параграфа, за исключением сведений, относящихся, к переменной IFS и параметру —, рассчитан также на интерпретатор С shell. — JP] Командная строка интерпретатора Bourne shell может содержать такую опцию, как -е (выход, если одна из команд возвращает ненулевой код завершения). В ней. могут присутствовать и другие аргументы, передаваемые сценариям. Параметры командной строки устанавливаются либо во время ввода интерактивных команд (по приглашению), либо в сценарии. Чтобы переустановить параметры командной строки, наберите set и новые значения. Так, например, интерпретатор shell будет показывать командные строки после их ввода и замены метасимволов, если установить опцию ^v (&.пу. $ set -v $ mail $groupl < maaaage mail andy ellen heather Steve wilma < message $ mail $group2 < maaaage mail jpeek@jpeek.com randy@xyz.edu yory@mongo.medfly.com < message $ set +v Ввод команды set +V отменяет опцию v в большинстве версий интерпретатора Bourne shell. Параметрами командной строки могут быть имена файлов или любые, другие строки, полученные, интерактивно или из сценария. Эта особенность делает удобными сохранение и анализ вывода команды UNIX с помощью обратных кавычек (э.щ. Можно, например, получить "список зарегистрированных пользователей в качестве значений параметров $1, $2 и т.д. Применяйте команду users, если она есть в вашей системе. В противном случае используйте команду who (Si.oy, а также команду cut (35.Н), чтобы удалить все, кроме имен пользователей: $ set 'users' $ set "who | cut -cl-B" for 44.16 $ for u > do > ...выполнение каких-либо действий с каждым именем пользователя ($и)... > done Первоначальные параметры можно сохранить в другой переменной и восстановить в дальнейшем: oldparms="$*" set нощые_параметры ...использование новых установок... set $oldparms Если первый параметр команды set начинается с дефиса (например, -е)-, интерпретатор shell рассматривает его как свою опцию, а не как строку, которую нужно помешать в параметры командной строки. Во избежание этого используйте два дефиса в качестве первого параметра команды set. В следующем примере параметр $1 получает значение -е, а имена файлов, соответствующие шаблону, поступают в параметры $2, $3 и т.д.: set — -e file* Так как интерпретатор shell анализирует и просматривает новые параметры перед их сохранением, метасимволы (&щ и другие специальные символы (S. 19) будут интерпретироваться, поэтому не забывайте защитить их с помощью кавычек или обратной косой черты (s.u). Это нужно учитывать при разбивке строк текста на части, которые не разделены обычными пробелами или символами табуляции (например, строки из базы данных с полями, 728 Часть восьмая. Программирование в среде shell
I шо разделенными двоеточиями). Новый разделитель можно задать путем установки переменной [FS (3S.2J) перед, использованием команды set. Если вы хотите сохранить в неизмененном виде какую-либо часть исходной командной строки, будьте осторожны. Например, если параметр $1 имел значение John Smith, после восстановления оно будет разбито: $1 получит значение John, a $2 — Smith. Гораздо лучшим решением является запуск порожденного процесса (п.от) для выполнения той части сценария, в которой необходимо переустановить параметры командной строки: # восстановление параметров командной строки # только в порожденном интерпретаторе shell: .(set новые параметры ... выполнение каких-либо действий с новыми параметрами... У # исходные параметры далее не изменяются... И последнее замечание: команда set не изменяет значение параметра $0 — имя файла сценария. - JP 44.20 Команда test: проверка файлов и строковых переменных В UNIX имеется команда test, применяемая для выполнения многих полезных проверок. Например, прежде чем сценарий попытается выполнить запись, эта команда проверяет, доступен . ли файл для записи. Она может рассматривать строку в переменной интерпретатора shell как число и делать сравнения ("меньше ли это число 1000?"). Вы можете производить комбинированные проверки ("если файл существует и доступен для чтения и номер сообщения больше 500..."). Различные версии команды Cert выполняют неодинаковое число проверок. Для получения подробной информации читайте руководство по вашему интерпретатору shell (если он содержит встроенную о-Ю) команду test) или страницу интерактивного руководства test{\). Команда test возвращает нулевой код завершения (44.07), если результат проверки — истина, и ненулевой код в противоположном случае. Поэтому команда test используется с операторами if, while и until. Вот как ваша программа может проверить, имеется ли в начальном каталоге пользователя файл с именем .signature, который доступен для чтения: SHOMEftftJ if test -r $HOME/.signature then ...Выполнение каких-либо действий... else imytiame 44.22 echo "$myhame: Can't read your '.signature'. Quitting:"' 1>S2 exit 1 £i Команда test позволяет также проверять выражение, не являющееся истинным. Для этого перед проверяемым условием необходимо поставить восклицательный знак. Например, при выполнении следующей проверки возвращается значение "истина", если файл .signature недоступен для чтения: if test ! -г $НОМЕ/.signature then echo "$myname: Can't read your '.signature1. Quitting." 1>&2 exit 1 fi В UNIX существует версия команды test с именем / (на самом деле это ссылка на эту же программу). Да, это левая квадратная скобка. Она взаимозаменяема с командой test за исключением того, что в конце тестовой конструкции должна находиться правая квадратная скобка. Второй из приведенных выше примеров можно переписать так: if- [- ! -г-$НОМЕ/.signature ] then Программирование для начинающих 729
44,21 echo "$myname: Can't read your '.signature'. Quitting." 1>&2 exit 1 fi He забывайте ставить пробелы между скобками и текстом. С пустыми аргументами связан ряд других проблем, пути решения которых описаны в параграфах 46.04 и 46.05. (В стремлении (ф) J к полноте функциональных возможностей воспользуйтесь GNU-версией команды test, " находящейся на компакт-диске. Если же вас интересует скорость, рекомендуем использовать test встроенную в shell версию.) - JP 44.21 Выбор имени новой команды При написании новой программы или сценария важно удостовериться, что ее имя не будет совпадать ни с какой другой командой в системе. Например, необходимо проверить, существует ли команда с именем tscan. Для этого следует ввести одну из команд следующего примера. Если от одной из них поступят сообщения, то, вероятно, команда с таким именем уже существует. (Команда type работает в интерпретаторах ksh, bash и во многих версиях Bourne shell. Я показал это с помощью знака доллара ($) в приглашении.) % man 1 tscan No manual entry for tscan in section 1. which 50.0» ■% which tscan no tscan in . /xxx/ehuser/bin /usr/bin/Xll /usr/local/bin ... whereis 50.05 % whereis tscan tscan: alius /at!2 % alias tscan % $ type tscan tscan not found - JP 44.22 Определение имени программы и множественные имена программ Любая UNIX-программа должна использовать свое имя в качестве первого слова в выводимом ею сообщении об ошибке. Это важно, когда программа выполняется в фоновом режиме или в составе конвейера, ведь нам необходимо знать, какая из программ является источником проблемы: имя_программы: quitting: can't read file xxxxxx Заманчиво использовать имя программы в команде echo: echo "имя_программы: quitting: can't read file $file" 1>&2 Однако если вы когда-нибудь измените имя программы, то наверняка забудете исправить и сообщение. Неплохим решением является сохранение в начале сценария имени программы в переменной интерпретатора shell, которую впоследствии можно использовать во всех сообщениях: myname= имя_программы echo "$myname: quitting: can't read file $file" 1>&2 Еще лучше использовать параметр $0. Интерпретатор shell автоматически помещает туда имя сценария. Однако $0 может содержать полное путевое имя сценария, такое как /xxx/yyy/bin/some- prog. Команда basename (4S.U) решает эту проблему: она удаляет начало путевого имени — все, кроме имени файла. ПО Часть восьмая. Программирование в среде shell
44.23 Если, например, $0 равно /u/ehuser/bin/sendit, то команда myname=""basename $0'" поместит только имя sendit в переменную интерпретатора shell myname. При помощи ссылок (ia.03) можно дать несколько имен своим программам (ts.u), а не только файлам UNIX. Примером является сценарий с именами //, If, lg (и т.д.), приведенный в параграфе 16.07. Используйте параметр $0 для получения текущего имени программы. - JP 44.23 Чтение файлов с помощью команд . и source Как объяснялось в параграфе 6.02, UNIX-программы никогда не изменяют среду своих родителей. Программа может изменить только ту среду, которая впоследствии будет передана ее потомку. Среди начинающих пользователей UNIX распространена одна ошибка: они пытаются написать программу, которая меняет каталог (или выполняет другие операции, связанные с переменными среды), а затем пробуют определить, почему она не работает. Так делать нельзя. Если вы пишете программу, выполняющую команду cd, результат работы этой команды будет действительным только в вашей программе. Когда она завершится, вы вернетесь в свой исходный (родительский) интерпретатор shell. Одним из решений является запуск сценария с помощью команды source (для csh и bash) или команды . (в sh, ksh, а также в bash). Например, если файл с именем change-my-directory содержит команду cd /somewhere/else то с помощью следующих команд можно сменить каталог текущего интерпретатора shell: % source change-my-directory $ . change-my-directory Команды source и . выполняют сценарий в текущем интерпретаторе shell, а не запускают порожденный интерпретатор. Эти команды работают только с файлами сценариев shell. Команды source и . нельзя использовать для запуска в интерпретаторе shell двоичных (непосредственно исполняемых) файлов. Если ваш интерпретатор shell не поддерживает функции (юм), их можно имитировать (io.io) с помощью команды ., которая работает во многом так же, как и подпрограмма или функция. - ML, JP Программирование для начинающих 731
45 Программирование для подготовленных пользователей 45.01 Для тех, кто знаком с основами В настоящей главе описан ряд трюков и приемов программирования для интерпретатора Bourne shell. Некоторые из них документированы, но их описания тяжело найти в документации, а другие вообще не отражены в ней. Ознакомьтесь с кратким содержанием параграфов данной главы. • Первая группа параграфов посвящена созданию исполняемого файла с символами #! в первой строке. Во многих версиях UNIX (см. параграф 44.04) исполняемый файл может начинаться с такой строки: #!/луть/имя_интерпретатора Ядро запустит программу, указанную в этой строке, и предоставит ей файл для "чтения. В своей классической заметке в Usenet, включенной в параграф 45.02, Крис Торек рассказывает, как появилось выражение #!". В параграфе 45.03'вы узнаете, что интерпретатор shell может не понадобиться сценариям вообще. Параграф 45.04 немного вас позабавит, так как в нем приведены необычные примеры использования выражения #!. Примеры из параграфа 45:05 продемонстрируют, каковы функции этого выражения. Если в вашей UNIX-системе данное выражение не употребляется, небесполезным окажется параграф 45.06, в котором описан трюк, позволяющий выполнять сценарий с помощью интерпретатора Bourne shell. Сценарии, выполняемые с помощью интерпретатора, отличного от shell, приведены в параграфах 25.11, 25.12 и 35.08. • Следующие пять параграфов посвящены процессам и командам. 'Команда exec (параграф 45.07) заменяет интерпретатор shell другим процессом. Ее.'.можно использовать и для перёадресации ввода-вывода. Команда trap может управлять реакцией порожденных процессов на сигналы (параграф 45.08). Оператор" : (двоеточие) вычисляет свои аргументы и возвращает нулевой код завершения; в параграфе 45.09 вы узнаете, почему необходимо следить за этим. UNIX хранит на диске исходную копию файла после его открытия. Как показано в параграфе 45:10, это имеет как достоинства, так и недостатки. Команда jot (параграф 45.11) удобна при выполнении всех видов операций с числовыми и символьными списками. 732 Часть восьмая. Программирование в среде shell
• 45.01 • Далее рассматриваются методы обработки переменных и параметров. Подстановка параметров, описанная в параграфе 45.12, представляет собой быстрый способ проверки, установки и присвоения переменным исходных значений. Как использовать параметр $0 и ссылки, чтобы обеспечить наличие нескольких имен у одного сценария и выполнение им различных функций, вы узнаете в параграфе 45.13. Параграф 45.14 демонстрирует простой способ получения последнего аргумента командной строки. В параграфе 45.15 описан один из способов удаления всех аргументов командной строки. • Операторам цикла интерпретатора sh мы посвятили четыре параграфа. Цикл for обычно считывает список одиночных аргументов в единственную переменную интерпретатора shell. В параграфе 45.16 показано, как задать в цикле for чтение стандартного ввода. Параграф 45.17 содержит описание способов установки нескольких переменных в цикле for. Команды dimame и basename позволяют в цикле разделять путевые имена на составляющие. Об этом мы поговорим в параграфе 45.18. Вы узнаете также, что в начало цикла while можно помещать несколько командных строк (параграф 45.19). • В следующих параграфах освещены разнообразные аспекты ввода-вывода. Параграф 45.20 знакомит с различными способами открытия файлов и с дескрипторами файлов (о стандартных потоках ввода-вывода и ошибок, оказывается, можно узнать больше, чем кажется на первый взгляд). В параграфе 45.21 кратко изложены принципы использования дескрипторов файлов в интерпретаторе Bourne shell, а также описан процесс переадресации стандартных потоков ввода-вывода и ошибок. Интерпретатор shell позволяет единовременно переадресовать ввод-вывод всех команд цикла. В параграфе 45.22 демонстрируется применение этого приема, а в параграфе 45.23 анализируются его достоинства и недостатки. • Shell может читать команды непосредственно из файла сценария. Как говорится в параграфе 45.24, этот интерпретатор способен также читать команды со своего стандартного ввода, что, однако, может вызвать некоторые проблемы. В параграфе 45.25 мы рассмотрим на примере, в каких случаях удобно читать сценарий из потока stdin: здесь описан сценарий, который во время выполнения создает другой сценарий. Следующие два параграфа посвящены вопросам ввода-вывода. Одной из ловушек при использовании конструкции "документ здесь" (предназначена для переадресации ввода из файла сценария) является применение различных операторов, обозначающих окончание данной конструкции в интерпретаторах Bourne shell и С shell (см. параграф 45.26). В параграфе 45.27 показано, как отменить вывод на экран, когда сценарий читает какой-то секретный ответ пользователя, например пароль. • Параграфы 45.28, 45.29 и 45.30 иллюстрируют использование команды ехрг для обработки выражений. Параграф 45.31 посвящен вопросам подстановки результатов выполнения команд (9.щ. Команда grabchars, описанная в параграфе 45.32, подобна команде read (44.13), однако она не требует нажатия клавиши [RETURN] после ввода ответа. Кроме того, команда grabchars может выводить сообщения и выполнять несложную проверку ответов. В параграфе 45.33 показан трюк, обеспечивающий одновременную проверку двух условий одним оператором case (44.os). Удобный способ имитации массивов в интерпретаторе Bourne shell рассматривается в параграфе 45.34. Как посредством команд echo и tr получить в сценарии управляющий символ, не вводя его в файл, вы узнаете в параграфе 45.35. Наконец, в параграфе 45.36 описан простой способ получения монопольного доступа к файлу или другому системному ресурсу. - JP Программирование для подготовленных пользователей 733
45.02 45.02 Сказка об операторах:, # и #/ Жил-был Интерпретатор Bourne shell. Поскольку он был один, то не нужно было думать, как запускать сценарии: их запускали с помощью Интерпретатора Bourne shell. Все работало, и все были счастливы. Однако пришел Прогресс, и кто-то написал другой интерпретатор shell. Люди подумали, что это хорошо и что теперь у них есть свобода выбора. Кто-то выбрал один интерпретатор, кто-то — другой. Каждый писал свои сценарии, и все были счастливы. Но однажды кто-то запустил сценарий, написанный для другого интерпретатора shell, и, увы, потерпел крах. Люди возопили и позвали на помощь своего Повелителя. "Ладно, — сказал Повелитель, — я помогу вам. Интерпретаторы shell являются несовместимыми. Мы должны сделать так, чтобы они знали, какой интерпретатор должен запускаться для выполнения нужного сценария. Внемлите: один интерпретатор имеет псевдокомментарий с именем :, а другой — настоящий комментарий с именем #. Посему повелеваю, что отныне и впредь один интерпретатор будет выполнять сценарии, начинающиеся с :, а другой — начинающиеся с #." И было так, и все были счастливы. Но Прогресс не замедлял свой шаг. Со временем стало очевидным, что если бы ядро могло выполнять сценарии, то все были бы счастливы. Кто-то написал дополнительный код, и сценарии стало выполнять ядро, но, к сожалению, только тогда, когда они начинались с магического заклинания #!, которое подсказывало ядру, какой интерпретатор должен выполнить сценарий. И было так, и все были растеряны. Ибо конструкция #! выглядела как комментарий. Хотя ядро могло видеть #1 и запускать сценарий, оно делало это только тогда, когда были установлены определенные волшебные биты. И если заклинание произносилось неверно, ядро останавливалось. Люди вновь воззвали к Повелителю о помощи, но тот молчал. И было так, и так есть до сих пор. Как бы там ни было, лучше использовать #! /bin/sh ИЛИ #! /bin/csh в качестве первой строки сценария. Строка tH /bin/csh -f также иногда полезна [она обычно выполняется быстрее, поскольку интерпретатор csh не читает файл xshrc (2.02) — JP\. - СТ 45.03 Для выполнения вашего сценария не нужен интерпретатор shell? Не запускайте его Если ваша UNIX-система понимает (44М) файлы, начинающиеся со строки # ! I каталог/'программа то для запуска интерпретатора shell не нужно использовать такие строки, как #!/bin/sh. Допустим, ваш сценарий предназначен для запуска программы, например awk. В этом случае UNIX может запустить эту программу напрямую, что позволит сэкономить время. Такой прием особенно удобен при использовании маломощных или перегруженных компьютеров, а также при неоднократном вызове сценария (например, в цикле). Рассмотрим два сценария, которые выводят на печать второе слово каждой строки текстового файла. В одном используется интерпретатор shell, а в другом осуществляется непосредственный запуск программы awk: % cat with_sh #!/bin/sh awk ' { print $2 } ' $* 734 Часть восьмая. Программирование в среде shell
45.04 % cat no_eh #!/usr/bin/awk -f { print $2 } % cat aflle one two three four five Запустим оба сценария и оценим время их выполнения с помощью команды time (39.02/. % time with_eh afile two O.lu 0.2s 0:00 26% % time no_sh afile two O.Ou 0.1s 0:00 13% Важно учесть одно обстоятельство. Когда ядро запускает программу из командной строки интерпретатора, последний получает имя сценария в качестве аргумента. Если интерпретирующая программа понимает, что это имя файла (как в случае /bin/sti), то в программе не требуется предпринимать дополнительные действия. Однако такие программы, как awk и sed, требуют указать опцию -/. если предполагается, что они будут читать свой сценарий из файла. Это делает несколько странным синтаксис вызова интерпретирующей программы, что мы продемонстрировали в предыдущем примере, где вызывается команда awk -/без последующего имени файла. Сам сценарий является входным файлом! При употреблении такого синтаксиса интерпретирующая программа должна воспринимать символ # как комментарий, иначе строка выбора интерпретатора будет обработана (и, возможно, отвергнута) самим интерпретатором. (К счастью, интерпретаторы shell, программы perl, sed и awk распознают символ комментария.) - JP 45.04 Поиграем с #\ [У Вас может сложиться мнение, что "магические" символы #! предназначены только для сценариев shell. Неправда (45.05, 45.оз)\ Приведем несколько забавных примеров. Изучите их и, если ваша UNIX-система понимает # !, выполните. Я надеюсь, что они помогут вам понять, что на самом деле делает оператор #!. — JP] Вопрос: Почему сценарии начинаются с #!/bin/sh или #!/bin/csh? Ответ: В некоторых системах, особенно в тех, что разработаны под влиянием BSD UNIX, таким образом обеспечивается возможность непосредственного выполнения программ. Это означает, что ядро может запустить программу, несмотря на то что она не является машинным кодом. Ядро вызовет указанную программу после непродолжительной обработки аргументов. Например, сценарий #! /bin/mv переименовывает сам себя. Поместите его в файл с именем zap, наберите zap zup, и вы получите сценарий с именем zup. Ваш сценарий попытался выполнить программу посредством команды exec с аргументом zup. Это ему удалось, хотя на самом деле выполнялась команда /bin/mv с аргументами zap zup. Сценарий может быть самоудаляющимся: #! /bin/rm или самораспечатывающимся: #! /bin/awk NR>1{print} текст... Последний сценарий работает, потому что ядро вставляет не только имя файла в список аргументов, но и дополнительный аргумент. Одни системы допускают применение только Программирование для поцготовпенных попьзоватепен 735
45.05 одного такого аргумента (содержащего пробельные символы), в то время как другие ризрешвют использовать несколько аргументов, разделенных пробельными символами (символами пробела и табуляции). Обратите внимание, что в последнем примере не предусмотрен способ включения в аргумент пробельных символов (ядро вряд ли обеспечивает защиту специальных символов, как в интерпретаторах shell). Безопаснее избегать употребления пробельных символов. Как правило, данный прием используется с опцией -/ интерпретатора С shell (fast — быстрый, без чтения файла .cshrc), но вполне применим и в программе awk. Оператор #! описан (хотя и недостаточно полно) на странице руководства ехесме{2). Заметим, что обычно количество символов в строке с конструкцией #! ограничено, чаще всего оно составляет 32. (32 является "магическим" числом, поскольку оно равно значению sizeof (struct exec).) - СТ 45.05 Файл, который сам показывает свое содержимое, или что делает оператор #/ Если ваша UNIX-система понимает (44.04) исполняемые файлы, начинаюигиеся с 4!, можно прибегнуть к элегантному приему создания исполняемых файлов, которые демонстрируют свое содержимое (или его часть). Я пользовался им для создания программы help в системе, которая не обладала функциями интерактивной справки. Такая программа, как cat (2S.02), не совсем подходит, поскольку вместе с сообщением она отображает строку с #!. Смотрите, что происходит: % cat help #!/bin/cat For help with UNIX, call the ACX Consulting Hotline at 555-1212. man command shows the manual for a command % chraod -1-х help % help #!/bin/cat For help with UNIX, call the ACX Consulting Hotline at 555-1212. man command shows the manual for a command Фокус в том, чтобы вызвать программу, которая показывает все строки, кроме строки, начинающейся с #!. В следующем файле, например, для удаления первой строки используются редактор sed (34.24) и его команда d: % cat help #!/bin/sed Id For help with UNIX, call the ACX Consulting Hotline at 555-1212. man command shows the manual for a command % help For help with UNIX, call the ACX Consulting Hotline at 555-1212. man command shows the manual for a command К файлам большего объема можно применить команду тоге +2 (25.Ш). Следующий сценарий показывает себя поэкранно, начиная со строки 2: % cat help #!/usr/ucb/more +2 For help with UNIX, call the ACX Consulting Hotline at 555-1212. man command shows- the manual for a command 736 Часть восьмая. Программирование в среде shell
45.07 Необходимо использовать полное путевое имя интерпретатора, поскольку ядро не просматривает вашу последовательность поиска (s.m). Ядро может передавать интерпретатору только один аргумент. При наличии нескольких аргументов вы не сможете воспользоваться указанным приемом. В следующем примере я попытался передать команде grep два аргумента, однако ядро передало всю строку -v Л# как один аргумент. Это запутало команду grep, которая "обжаловала" каждый символ, начиная с пробела: % cat help #!/bin/grep -v Л# For help with UNIX, call the ACX Consulting Hotline at 555-1212. man command shows the manual for a command % help grep: illegal option — grep: illegal option — * grep: illegal option — # Usage: grep -hblcnsvi pattern file ... (Помните, что в этом сценарии интерпретатор shell не обрабатывает аргументы. Это делает ядро.) - JP 45.06 Обеспечение работы сценария без интерпретатора Bourne shell Многие версии UNIX позволяют начинать сценарии следующим образом: #!/bin/sh Такой исполняемый файл всегда будет читаться интерпретатором Bourne shell. Если некоторые версии UNIX не понимают оператор fr! (44.04), то сценарии можно начинать так: #!/bin/sh || 44.09 export PATH | | exec /bin/sh $0 $argv:q Если эту строку читает интерпретатор Bourne shell (т.е. fr! /bin/sh работает), команда export PATH работает, а остальная часть командной строки опускается. Если же строку читает интерпретатор С shell, он выводит сообщение об ошибке такого содержания: export: Command not found. Далее он выполняет команду exec /bin/sh $0 $argv:q. Команда exec (45.07) заменяет С shell интерпретатором Bourne shell, передает ему имя файла сценария (параметр $0), а также список заключенных в кавычки аргументов командной строки (параметр $argv:g (9.об>). - JP 45.07 Команда exec Команда exec выполняет команду вместо текущего интерпретатора shell, т.е. она прекращает работу текущего интерпретатора и вместо него запускает новый процесс (зв.оз). Команда exec часто использовалась для выполнения последней команды сценария. При этом shell уничтожался несколько раньше, чем завершалась последняя команда. Подобная практика позволяла сэкономить один процесс и немного памяти. (Неправда ли, вы рады, что используете современную систему? Ведь в такой системе более не нужны ухищрения такого рода, если только в ней не ограничено количество процессов, которые может запускать каждый пользователь.) Команду exec можно применять для замены одного интерпретатора shell другим % exec ksh $ не заботясь дополнительно о том, чтобы неиспользуемый shell ждал завершения работы нового интерпретатора. Программирование для подготовленных пользователей 24 9-171 737
45M Команда exec также манипулирует дескрипторами файлов (45.21,45.22) в интерпретаторе Bourne shell. Когда эта команда используется с дескрипторами файлов, она не заменяет текущий процесс. Следующая команда, например, назначает стандартным вводом всех команд файл formfile, а не терминал: exec < formfile - ML, JP 45.08 Обработка сигналов для порожденных процессов Команда trap (44.12) интерпретатора Bourne shell управляет действиями интерпретатора, когда он получает сигнал от команды kil[ (мю), от команды с клавиатуры ([CTRL-c] и т.д.) или от других процессов. Для запуска внешней команды (i.io) (команды вызова редактора или простой команды типа sort) интерпретатор shell активизирует порожденный процесс (зя.оз). Если программа, выполняемая в порожденном процессе, способна обрабатывать собственные сигналы, родительский процесс должен каким-то образом передать порожденному процессу эти сигналы. Допустим, вы запустили редактор W в порожденном процессе и хотите направить сигнал [CTRL-c] для остановки работы редактора W, но не желаете, чтобы по этому сигналу был уничтожен родительский процесс (в котором выполняется сценарий). Что должно произойти с родительским процессом при получении такого сигнала? Должен ли он "умереть" или продолжать свою работу? Интерпретатор Bourne shell довольно гибок в обработке сигналов. Плохо то, что в руководстве по интерпретатору sh ничего об этом не сказано. И ни в одном руководстве я не встречал описания такой полезной возможности, как употребление оператора : (двоеточие) (45.09) с командой trap. В табл. 45.1 показаны возможные варианты. Таблица 45.1. Аргументы команды trap (для большинства версий интерпретатора Bourne shell) Аргумент и ii "командная_строка" 'командная строка' Без аргументов Действие Игнорировать сигнал и не передавать его порожденному процессу (Недокументирован.) Проигнорировать сигнал и передать его порожденному процессу Выполнить командную строку, осуществляя подстановку переменных и результатов выполнения команд в момент определения команды trap; не передавать сигнал порожденному процессу Выполнить командную строку, осуществляя подстановку переменных и результатов выполнения команд в момент активизации команды trap; не передавать сигнал порожденному процессу Задать стандартную обработку сигнала (обычно родительский процесс прекращается); передать сигнал порожденному процессу Поскольку многое из того, что приведено в данной таблице, не документировано, я не буду пытаться ответить на вопросы о том, как работать в вашем интерпретаторе shell. Вместо этого я привел два сценария, которые позволят вам поэкспериментировать с обработкой сигналов. Первый сценарий, parent, запускает второй — child. Сценарий child устанавливает порядок обработки сигналов, а затем "засыпает" (sleep (40.02)) и пребывает в этом состоянии, когда вы посылаете ему сигнал. Это позволяет задавать сигнал прерывания с помощью клавиш [CTRL-c] или другое прерывание, если родительский процесс выполняется в интерактивном режиме, либо команду kill с номерами сигналов при выполнении процесса parent в фоновом режиме. Вы можете редактировать строки с командой trap в двух сценариях, чтобы проверить те установки, которые собираетесь использовать. Сначала приведем пример. Я запускаю сценарий parent в фоновом режиме из интерпретатора С shell, а затем посылаю ему сигнал с номером 1 (hangup — отбой): % parent £ [1] 8669 parent started child started, pid is 8671 %l I2.oi % kill -1 %1 738 Часть восьмая. Программирование в среде shell
45.09 child got a signal 1 child exiting parent still running after child exited ...спустя 1000 секунд.. . parent exiting [1] + Done parent А теперь — сценарии: % cat parent #!/bin/sh echo parent started trap "echo parent exiting; exit" 0 trap : 1 # передать сигнал 1 порожденному # процессу и не завершаться trap ""2 # игнорировать сигнал 2 и скрыть его # от порожденного процесса trap "echo parent got signal 15" 15 # игнорировать сигнал 15 и скрыть его # от порожденного процесса; # завершиться при поступлении # других сигналов, посылать их # порожденному процессу child echo parent still running after child exited sleep 1000 % cat child #! /bin/sh echo child started, pid is $$ trap 'echo child exiting; exit' 0 trap 'echo child got a signal 1' 1 trap ' ' 2 # игнорировать сигнал 2 trap 'echo child got a signal 3' 3 sleep 1000 # ожидать сигнала Даже с помощью приведенных примеров понять, как обрабатываются сигналы, непросто. Дополнительный материал по обработке сигналов ищите в руководстве по используемой вами версии системы UNIX. - JP 45.09 Бесценный оператор : в Bourne shell Некоторые думают, что : является символом комментария в интерпретаторе Bourne shell. Это не так. Указанный оператор вычисляет значения своих аргументов и возвращает нулевой код завершения (44.07). Рассмотрим, в каких ситуациях его можно использовать. • Замена команды true при создании бесконечных циклов while (44.щ. Это более эффективно, чем использование конструкции while true, поскольку интерпретатор shell не должен запускать новый процесс при каждом повторении цикла: while : do команды done Конечно, одной из команд должен быть оператор break, чтобы в конечном итоге цикл завершился. • Если необходимо использовать оператор else в конструкции i[ (44M) и при этом оставить пустым оператор then: if условие then : Программирование для подготовленных попьзоватепей 24* 739
45.10 else команды fi • Если в вашей версии интерпретатора Bourne shell не употребляется настоящий символ комментария #, можно использовать оператор :, чтобы сымитировать его. Правда, безопаснее применять кавычки. Тогда shell не предпринимает попыток интерпретировать в вашем "комментарии" такие символы, как > и |: : 'read answer and branch if < 3 or > 6' • Наконец, данный оператор удобен при подстановке параметров (45.12) в таких выражениях, как ${переменная?} и ${переменная=значение_по_умолчанию). Например, при использовании следующей строки в сценарии он выводит сообщение об ошибке и завершается, если не установлена любая из переменных USER и НОМЕ. : ${USER?) ${HOME?) - JP 45.10 Удаление открытого файла в целях безопасности и упрощения очистки диска Если процесс открыл файл (45.20), то UNIX не удалит этот файл, пока процесс не закроет его. (Команда rm удаляет из каталога только ссылку на файл, а не сам файл.) Я слышал доводы (24.03), опровергающие целесообразность удаления открытого файла. Если вы хотите запустить набор команд из файла, никому не позволив читать список используемых вами команд, напишите сценарий, который сам себя удаляет, прежде чем выполнит какие-либо другие команды. Правда, необходимо знать, что в случае использования сетевой файловой системы (1.зз) "удаленный" файл только переименовывается и становится скрытым (i6.ii) (его имя имеет следующую структуру: .nfsXXXXX). Вот пример самоудаляющегося сценария: % cat doit rm doit # теперь, когда интерпретатор shell открыл этот файл, # мы можем удалить его Is doit make biprog % sh doit Is: doit not found cc -target sun4 -c routine.с Вот типичный сценарий, открывающий и удаляющий файл в каталоге /tmp рюзу. % cat delme #!/bin/sh temp=/tmp/delme$$ # файл в каталоге /tmp (может быть в другом # каталоге) echo "This is linel. This is line2. This is line3." > $temp # три строки помещаются в $temp Is -1 $temp; wc $temp # вывод имени файла и подсчет строк в файле exec 45.07 exec < $temp # назначить стандартным вводом файл $temp read line; echo $line # чтение и отображение первой строки из $temp rm $temp; echo rm returned $? # удаление ссылки на $temp; отображение кода завершения Is -1 $temp; wc $temp # файл исчез...? read line; echo $line # но он все еще открыт! read line; echo $line <&.- 4SJI exec <&- # закрыть стандартный ввод (и файл) 740 Часть восьмая. Программирование а среде shell
45.U % delme -rw-rw-r— 1 jerry 45 Sep 16 12:31 /tmp/delme2274 3 3 9 4 5 /tmp/delme2274 3 This is linel. rm returned 0 Is: /tmp/delme22743: No such file or directory wc: cannot open /tmp/delme22743 This is line2. This is line3. - JP 45.11 Многоцелевая команда jot iot Команда jot является чрезвычайно мощным инструментом программирования. Те, кто однажды испытал эту команду, стали использовать ее постоянно, однако те, кто не знаком с ней, могут быть сбиты с толку тем, как она работает. По этой причине, как мне кажется, лучше изучить команду jot на примерах. (Если вы читали другие мои параграфы, то могли заметить, что я считаю примеры лучшим способом изучения чего бы то ни было, а в отношении команды jot это верно вдвойне!) В простейшем случае команда jot выводит ряд чисел. Если предоставить ей одно целое число в качестве аргумента командной строки, она выведет последовательность чисел от 1 до указанного числа. % jot 4 1 2 3 4 "Да, большая работа", — скажете вы. Ладно, эта команда не может перевернуть мир, но зато здорово облегчит вам жизнь, если вы занимаетесь профаммированием для интерпретатора Bourne shell. Рассмотрим пример, в котором необходимо инкрементировать число в цикле. Обычно это делается так: counter=l while [ $counter -le 10 ] do counter=~expr $counter + 1' done Трудоемкий и медленный способ. Поскольку интерпретатор Bourne shell не имеет встроенных команд (i.io) обработки чисел, единственной возможностью инкрементировать счетчик является использование команды ехрг при каждой итерации цикла. Благодаря команде jot тот же цикл можно записать в простой конструкции for. for counter in 'jot 10s do done Команду jot можно использовать для представления любой другой последовательности чисел. Например, чтобы вывести целые числа от 24 до 28 включительно, введите: % jot 5 24 24 25 26 27 28 Программирование для подготовпенных попьзоватепвй 741
45.11 Первый аргумент (5) определяет количество значений, которые необходимо сгенерировать, а второй (24) задает начало счета. Это может обескуражить: а почему бы не ввести jot 24 28 для определения начала и конца последовательности, вместо того чтобы сначала вычислять, сколько чисел будет в последовательности? Причина заключается в том, что вам, может быть, не захочется генерировать все числа указанного диапазона. Третий аргумент команда jot воспримет как число, которым следует завершить последовательность. Если вы укажете либо начальную, либо конечную границу с десятичной точкой, то на выходе получатся дробные числа: % jot 5 24 28.0 24.0 25.0 26.0 27.0 28.0 % jot 4 24 28.0 24.0 25.3 26.7 28.0 Можно также использовать опцию -р, чтобы задать точность представления чисел: % jot -p4 4 24 28 24.0000 25.3333 26.6667 28.0000 По умолчанию выводимые значения располагаются на одинаковом расстоянии друг от друга в заданном интервале. Это можно изменить, используя четвертый числовой аргумент, который задает размер каждого шага итерации. Например: % jot 4 24 28.0 .5 24.0 24.5 25.0 25.5 Обратите внимание, что в данном примере показаны только первые четыре итерации, поскольку с помощью первого аргумента мы запросили четыре значения. Это вызвано тем, что три произвольных значения аргументов команды автоматически определяют четвертое. Если эти значения противоречат друг другу, команда jot останавливается после вывода четырех значений несмотря на то, что понадобилось бы девять итераций, чтобы завершить последовательность. Команда jot остановится также, если последовательность завершится до вывода указанного количества чисел. % jot 4 24 28 2 24 26 28 Чтобы опустить любое из этих значений, замените его одинарным дефисом. Например, если вам нужны целые числа от 24 до 28, можно опустить поле, задающее количество значений, поскольку в команде указан шаг 1: % jot - 24 28 1 24 25 26 27 28 742 Часть восьмая. Программирование а среде shell
45.11 Конечно, можно использовать отрицательные значения диапазона и отрицательные шаги: % jot -1-3-2 1 -1 -3 Чтобы получить вывод, разделенный символом, отличным от символа новой строки, используйте опцию -s. Например, для получения вывода предыдущей команды, разделенного пробелами, введите: % jot -s " " - 1 -3 -2 1 -1 -3 Вот такая она, команда jot, потенциально полезная для многих программистов. В действительности эта команда способна на большее. Используя опцию -с, вместо цифр можно вывести ASCII-символы (51.оз). Например, чтобы вывести ASCII-символ с кодом 65 (десятичное число), введите: % jot -с 1 65 А Для выполнения обратного преобразования, из ASCII в десятичный код, достаточно поставить символ на месте нижней границы: % jot 1 А 65 Это может пригодиться для получения списка всех букв английского алфавита: % jot -с 26 А А В С Опция -г обеспечивает получение случайных чисел, что очень удобно в интерпретаторах shell, не имеющих встроенного генератора случайных чисел (как Bourne shell и С shell). Чтобы создать 6-разрядное случайное число, введите: % jot -r 1 100000 999999 523467 (Для инициализации генератора случайных чисел можно указать четвертый аргумент.) Опция -Ь позволяет задать повторение заданного слова, что напоминает действие команды yes (23.ту. % jot -b lunchtime! 3 lunchtime! lunchtime! lunchtime! На страницах руководства по команде jot предлагается способ применения данной опции: когда требуется найти в файле строки длиной 40 и более символов, можно прибегнуть к регулярным выражениям; необходимо только сосчитать все точки: grep " " файл Но если же используете команду jot, то можете избежать формирования подобных длинных строк (и похвалить себя за то, что прочли документацию): gcep "jot -s "" -b . 40ч файл Самая мощная возможность командыуоЛреализуется посредством опции -и>. Аргументом этой опции является слово, содержащее символы преобразования формата, аналогичные тем, что Программирование для подготовпенных попьзователей 743
45.12 используются в функции printfQ (например, %d для вывода десятичных чисел и %h для вывода шестнадцатеричных чисел.) Если вы не знакомы с процессом преобразования форматов с помощью функции print/Q, прочтите страницу документации printftb) или загляните в любую книгу по С. Данная опция позволяет комбинировать строки с выводом команды jot, что удобно для манипулирования временными файлами в сценариях. Предположим, у нас есть сценарий, создающий множество временных файлов, которые необходимо удалять по завершении сценария. Возможно, вы даже создали в сценарии эти файлы с помощью команды jot, как показано выше: for counter in 'jot 10 1" do какие-нибудь команды > tmp$counter done Далее файлы должны быть удалены. Для этого можно выполнить еще один цикл, однако эффективнее просто ввести: rm 'jot -w tmp%d 10 1' Команда jot заменяет параметр tmp%d строками от tmpl до tmplO, которые являются именами временных файлов, созданных в сценарии ранее. - LM 45.12 Подстановка параметров Интерпретатор Bourne shell обладает набором операторов, которые делают удобными тестирование и установку переменных shell. Эти операторы описаны в табл. 45.2. Таблица 45.2. Операторы подстановки параметров интерпретатора Bourne shell Оператор | Действие ${переменная:-значение_по_умолчанию) ${переменная:=значенме_по_умолчанию} ${переменная:+другое_значение) ${переменная:? сообщение) Если переменная не определена или пуста, используется значение_по_умолчанию Если переменная не определена или пуста, ей присваивается значение_по_умолчанию, используемое впоследствии Если переменная определена и не пуста, используется другое значение Если переменная определена и не пуста, используется ее значение, иначе выводится сообщение (при условии что оно указано), а затем выполняется выход из интерпретатора shell; если сообщение опущено, выводится стандартное сообщение, которое зависит от используемого интерпретатора shell Если опустить двоеточие в одном из операторов из числа приведенных в табл. 45.2, интерпретатор shell не будет проверять, является ли переменная пустой. Иными словами, подстановка будет выполняться только в том случае, если переменная уже определена. (Некоторые первые версии интерпретатора Bourne shell не понимают двоеточия в операторе подстановки параметра.) Чтобы увидеть, как производится подстановка параметров, проанализируем простую версию сценария bkedit (uos, илу. #!/bin/sh if ср "?1" "$l.bak" then 744. Часть восьмая. Программирование в среде shell
45.13 $(VISUAL:-/usr/ucb/'vi) "$1" exit # КОД 'ЗАВЕРШЕНИЯ ОТ РЕДАКТОРА else echo "'basename $0' quitting: can't make backup?" 1>&2 exit 1 fi Если переменная среды VISUAL (б.оз) установлена и не пуста (например, ее значение — /usr/local/bin/emacs), строка сценария заменяется строкой /usr/local/bin/emacs "$1". Если же переменная VISUAL не установлена, командная строка имеет такой вид: /usr/ucb/vi "$1". Операторы подстановки параметров можно использовать в любой командной строке. Вы можете встретить их в конструкции с оператором : (45.09), которая предназначена для проверки того, определена ли переменная, или для задания значения по умолчанию. Ниже приведен пример. После первой подстановки (${nothing=default)) значение $nothing останется пустым, поскольку переменная уже установлена. При второй подстановке для $nothing будет установлено значение default, поскольку переменная установлена, но пуста. В результате третьей подстановки в качестве значения $something будет оставлено stuff. nothing= something=stuff : $ (nothing=default) : $(nothing:=default) : $(something:=de£ault) Интерпретаторы Korn shell и bash имеют схожие операторы редактирования строк (9.т>, например: ${переменная#%шаблон]. Эти операторы удобно применять в программах, а также в командной строке и в файлах конфигурации интерпретатора shell. - JP 45.13 Экономия дискового пространства и времени программирования: присвоение программе нескольких имен Если вы пишете: • несколько программ, выполняющих сходные задачи; • программы, в которых используются большие фрагменты одинакового кода (например, при написании второй, третьей и т.д. программ вы копируете строки из первой программы); • программу с несколькими опциями, которые вносят большие изменения в ее работу, то можете ограничиться написанием всего одной программы и созданием ссылок рем it.oj) на нее. Программа может определять имя, по которому ее вызвали, и, в зависимости от этого, выполняться особым образом с помощью команд case и test. Например, в Berkeley UNIX команды ex, v/, view и edit являются ссылками на один и тот же исполняемый файл. Это позволяет сэкономить дисковое пространство и упрощает сопровождение программ, но обычно имеет смысл только тогда, когда большая часть кода одинакова во всех программах. Если в программе заложено выполнение многочисленных проверок имен и множество особенностей для каждого варианта выполнения, данный подход приносит больше затрат, чем выгод. В зависимости от способа вызова сценария, его имя может быть как простым относительным путевым именем (например, prog или ./prog), так и полным путевым именем типа /usr/Joe/bin/prog (о путевых именах рассказывается в параграфе 14.02). Имя, под которым вызван сценарий, можно обрабатывать, двумя способами. Если в сценарии содержится один главный блок кода, как в сценарии If (I6.07), лучше всего для решения задачи подойдет оператор case, проверяющий значение $0. Метасимвол * в начале проверяемой строки (см. параграф 44.06) позволяет обрабатывать путевые имена всех видов, которые могут быть использованы для вызова сценария: case "$0" in * имя1) ...выполнить это, когда сценарий вызван как имя!... Программирование для подготовленных пользователей 745
45.14 *имя2) .выполнить это, когда сценарий вызван как имя2. *) ..вывести сообщение об ошибке и выйти, если $0 не совпадает с шаблоном... Можно также воспользоваться командой basename (4.1.1s), которая отсекает имена каталогов и сохраняет очищенное значение $0 в переменной myname. Значение $myname можно проверить в любом месте сценария, а также использовать в сообщениях об ошибках: myname='basename $0" case "$myname" in echo "$myname: aborting; error in xxxxxx" 1>&2 - JP 45.14 Определение значения последнего аргумента командной строки Хотите ли вы получить последний параметр из списка параметров $1, $2...[командной строки — JP]? Вот как это можно было бы сделать посредством команды eval \$$#: $ set foo bar baz tva\ S.ro $ eval echo \$$# baz если бы не возникала небольшая проблема с синтаксисом аргументов интерпретатора sh: $ set mnopqrstuvwx $ echo $11 ml Переменная $11 соответствует $(1}1, а не ${11}. А непосредственное использование переменной ${11} приводит к неправильной подстановке. Единственный надежный способ получения последнего параметра состоит в следующем: for i do last="$i"; done [В этом цикле for переменной last интерпретатора shell присваивается каждый параметр. По завершении цикла значение $last будет равно последнему параметру. Этот трюк не обязательно применять во всех s/г-подобных интерпретаторах. Интерпретаторы Кот shell и bash понимают запись типа $ (11}. — JP] - СТ, из телеконференции comp.unix.questions в Usenet, 15 января 1990 г. 45.15 Как отменить установку всех параметров командной строки Команда shift (м.щ убирает один параметр командной строки и сдвигает на одну позицию нумерацию остальных параметров. Чтобы удалить все параметры командной строки, содержащей три параметра, нужно выполнить команду shift три раза. Во многих интерпретаторах shell в команде shift можно указать аргумент (shift 3), который сообщает, сколько раз выполнять сдвиг. В таких интерпретаторах с помощью команды shift .$# (44.16) можно удалить все параметры. Возможно, универсальный способ отмены всех параметров состоит в установке единственного параметра посредством команды set (44.19) и в его последующем сдвиге: set X shift Установка единственного параметра приводит к удалению всех параметров, установленных ранее. - JP 746 Часть восьмая. Программирование в среде shell
45.17 45.16 Стандартный ввод в цикле for Обычно цикл for (44.16) в интерпретаторе Bourne shell применяется для поочередной обработки списка аргументов из командной строки или из переменной. Если операторы цикла скомбинировать с командой cat (25.02), заключенной в обратные кавычки (9.16), в цикле будут поочередно обрабатываться слова, поступающие со стандартного ввода. Рассмотрим пример: for х in 'cat' do ...обработка $х done Итак, данный способ обеспечивает разделение ввода на отдельные слова. При этом не имеет значения, сколько слов находится в каждой входной строке. В связи с этим описанный нами способ более удобен, чем запуск команды read в цикле while (см. параграф 9.20). Однако при использовании этого сценария в интерактивном режиме выполнение цикла не начинается, пока не завершится ввод, а цикл while read выполняется после ввода каждой строки. - JP 45.17 Цикл for с несколькими переменными Обычный цикл for (44.16) в интерпретаторе Bourne shell принимает список элементов, по очереди записывает элементы в переменную и запускает набор команд цикла для каждого элемента: for file in progl prog2 ргодЗ do ...обработка $file done Мне понадобился цикл for, который записывает набор элементов в различные переменные интерпретатора shell и выполняет каждый проход цикла для всего набора элементов (а не для одного элемента, как в обычном цикле for). Эту задачу решает следующий цикл: for bunch in "ellie filel6" "donna file23" "steve file34" do # ПОМЕСТИТЬ ПЕРВОЕ СЛОВО (ИМЯ ПОЛЬЗОВАТЕЛЯ) В $1, # А ВТОРОЕ (ИМЯ ФАЙЛА) - В #2... set 44.19 set $bunch mail $1 < $2 done Если у вас есть несколько аргументов командной строки и все они нужны, перед обработкой сохраните их в какой-нибудь переменной. Цикл можно построить следующим образом: for bunch in "u=ellie f=filel6 s='your files'" \ "u=donna f=file23 s='a memo'" "u=steve f=file34 s=report" do # УСТАНОВИТЬ $u (ИМЯ ПОЛЬЗОВАТЕЛЯ), # $f (ИМЯ ФАЙЛА) И #s (СОДЕРЖАНИЕ): eval $bunch mail -s "$s" Su < $f done В этом сценарии использована команда eval (s.ro) интерпретатора shell, предназначенная для повторного просмотра содержимого переменной bunch и его сохранения в отдельных переменных. Обратите внимание на одинарные кавычки в выражении s='your files': они группируют слова для команды eval. Интерпретатор shell удаляет эти кавычки перед сохранением значения в переменной s. - JP Программирование цпя подготовленных пользователей 747
45.18 45Л8 Использование команд basename и dirname ^^тш Почти каждая команда UNIX может использовать относительные и полные путевые име- | c#i 1 нш (Ы.02) для поиска файла или каталога. Иногда необходима часть путевого имени — ^_^ начальная (все до последней косой черты) или заключительная (имя за последней косой basename, чертой). Получить ее позволяют утилиты basename и dirname, имеющиеся в большинстве Игнате UNIX-систем (а также на компакт-диске). Общие сведения Команда basename удаляет все составляющие путевого имени файла, относящиеся к "пути", оставляя лишь "чистое" имя файла. Например: % basename /usr/bin/gigiplot gigiplot % basename /home/mikel/bin/bvurns.sh bvurns.sh Команда basename позволяет также удалить суффикс имени файла. Например: % basename /home/miJeel/bin/bvurns.sh .sh bvurns Команда dirname удаляет имя файла, оставляя ту часть путевого имени, которая описывает каталог: % dirname /usr/bin/screenblank /usr/bin - - % dirname local Если предоставить команде dirname "чистое" имя файла (как во втором примере), она сообщит, что данный файл находится в каталоге . (в текущем каталоге). Примечание: Во многих реализациях System V команды dirname и basename содержат ошибку. Они не распознают второй аргумент как суффикс, который нужно удалить. Вот хороший тест: °, basename 0. fоо . fоо Если в ответ вы получили 0, значит, используемая вами реализация команды basename работает исправно, а если 0. f оо — она содержит ошибку. Помните: если команда basename не работает, то команда dirname также не будет работать. Использование в циклах Рассмотрим пример использования команд basename и dirname. Некоторые каталоги содержат очень большие файлы — объемом свыше 100000 символов. Вы должны найти такие файлы, разделить их на части с помощью команды split (js.es) и добавить префикс huge, в начало их исходных имен. По умолчанию команда split присваивает частям файла имена xaa, xab, xac и т.д. Вместо х нужно использовать исходное имя файла и точку: for path in "find /home/you -type f -size +100000c -print' do и 4409 cd 'dirname Spath" I I exit exit 44.11 filename=' basename $path~ split $filename $filename. mv -i $filename huge.$filename done Команда find выводит путевые имена следующего вида: /home/you/somefile /home/you/subdir/anotherfile 748 Часть аосьмая. Программирование а среде shell
45.20 (Важно то, что в данном случае получены полные путевые имена. При получении относительных путевых имен команда cd не выполняется.) Команда cd использует вывод команды dimame для перехода в каталог, где находится нужный файл. Переменная filename, содержащая вывод команды basename, применяется в нескольких местах, в том числе дважды в командной строке split. Если при этом выдается сообщение об ошибке command too long, вместо первых двух строк вставьте строки, приведенные ниже. Они создают цикл с переадресацией ввода (45.22)-. find /home/you -type f -size +100000c -print | while read path - JP, ML 45.19 Цикл while с несколькими командами управления циклом Большинство пользователей считает, что цикл while (44. ю) в интерпретаторе Bourne shell всегда содержит одну команду управления циклом и имеет следующий вид: while команда do другие команды... done Однако команда на самом деле может быть списком команд. Код завершения последней команды используется для управления циклом. Это удобно при выводе запросов пользователю и чтении его ответов. Если пользователь, ничего не вводя, нажимает клавишу [ENTER], команда read возвращает false, а цикл завершается: while echo "Enter command or CTRL-d to quit: \c" read command do ...обработка $command done В следующем цикле запускается команда who и выполняется поиск в ее выводе. Если команда grep возвращает нулевой код (из-за того что она не нашла значение $who в $tempfile), выполнение цикла прекращается. В противном случае производится сложная обработка: while who > $tempfile grep "Swho" $tempfile >/dev/null do ...обработка $tempfile... done - JP 45.20 Открытие файлов и дескрипторы: обзор [Данный параграф содержит сжатый материал по указанной теме. Поэтому специалистам, которым требуется подробная информация, придется обратиться к специальному изданию, посвященному программированию в UNIX. — JP] Интерпретаторы shell в UNIX позволяют переадресовывать ввод и вывод программ с помощью операторов > и |. Как это происходит и как лучше использовать данную возможность? Предлагаем вашему вниманию обзор по этой теме. Когда ядро UNIX запускает процесс (жоз), например grep. Is или интерпретатор shell, оно предоставляет ему несколько файлов для записи и чтения (рис. 45.1). Программирование для подготовленных пользователей 74$
45.20 % grep /dev/tty F.D. 1 (Стандартный поток вывода) F.D. 2 (Стандартный поток ошибок) F.D. О (Стандартный поток ввода) ' w' J.&.S^Av^v v'::-:;.:::.v:;.;:--: F.D. — дескриптор файла Рис. 45.1. Открытие стандартных потоков ввода-вывода без команд переадресации Ядро присваивает каждому файлу номер, или дескриптор. Однако, как правило, применяются имена файлов, а не номера. • Стандартный поток ввода, или stdin (дескриптор файла 0), связан с файлом, откуда процесс читает данные. Это может быть текстовый файл или поток данных с клавиатуры. • Стандартный поток вывода, или stdout (дескриптор файла 1), связан с файлом, куда процесс записывает свои ответы. • Стандартный поток ошибок, или stderr (дескриптор файла 2), связан с файлом, куда процесс посылает сообщения об ошибках. Как показано на рис. 45.1, по умолчанию файл, открытый для потоков stdin, stdout и stderr, имеет имя /dev/tty — имя вашего терминала. Это удобно ках пользователям, так и программистам. Пользователю не придется указывать программе, откуда читать и куда записывать данные, поскольку по умолчанию для этого используется терминал. А программисту удастся избежать (во многих случаях) открытия файлов для чтения и записи — программы могут непосредственно читать из stdin, записывать в stdout и посылать сообщения об ошибках в stderr. Возможен другой вариант. Когда интерпретатор shell запускает процесс (при вводе команды по приглашению), можно сообщить ему, какой файл "подключить" к потокам с упомянутыми дескрипторами. Например, на рис. 45.2 показано, что произойдет при запуске команды grep, если указать интерпретатору shell переадресовать ее стандартный вывод с терминала в файл grepout. gxepout % grep something aomefile > grepout F.D. 1 (Стандартный поток вывода) -файл /dev/tty F.D. 2 (Стандартный поток ошибок) F.D. О (Стандартный поток ввода) F.D. — дескриптор файла Рис. 45.2. Стандартный вывод, переадресованный в файп 750 Часть восьмая. Программирование в среде shell
45.21 Для чтения и записи программы могут использовать обычные файлы, а не только потоки stdin, stdout и stderr. С помощью команды grep, приведенной на рис. 45.2, открыт файл somefile. Эта команда не использовала никаких стандартных дескрипторов для обращения к данному файлу. По соглашению, принятому в UNIX, если в командной строке не указано имя файла, программа читает данные со своего стандартного ввода. Программы, работающие таким образом, называются фильтрами о.щ. Все интерпретаторы shell выполняют элементарную переадресацию потоков данных stdin, stdout и stdeir. Но, как указано в параграфе 45.21, интерпретатор Bourne shell наряду с этими потоками использует дескрипторы файлов с номерами от 3 до 9. В некоторых случаях это удобно. • Вероятно, у вас есть несколько файлов данных, которые нужно использовать для записи или чтения. Чтобы не вводить их имена, можно прибегнуть к дескрипторам файлов. • После открытия файла ядро запоминает, в каком месте в последний раз выполнялось чтение или производилась запись. Пока файл открыт, каждый раз при использовании дескриптора вы будете обращаться к одному и тому же месту файла. Это особенно важно, если в процессе чтения или записи файла вы применяете несколько программ. Например, в некоторых UNIX-системах команда line читает из файла одну строку. Эту команду можно вызывать снова и снова, когда нужно прочесть следующую строку. После открытия файла можно удалить из каталога ссылку на него (имя) (45.Ю). Процесс способен обращаться к файлу по его дескриптору без использования имени. • Когда UNIX запускает новый порожденный процесс (моз), дескрипторы открытых файлов передаются этому процессу. Порожденный процесс может читать файлы и записывать в них данные, используя дескрипторы файлов, открытых родительским процессом. Следует отметить, что цикл с переадресацией ввода-вывода, описанный в параграфах 45.22 и 45.23, имеет преимущества перед тем циклом, что приведен в настоящем примере. - JP 45.21 Оператор п>&т: переадресация стандартных потоков вывода и ошибок По умолчанию стандартный поток ошибок направляется на терминал. Стандартный поток вывода либо направляется на терминал, либо переадресовывается (в файл, канал или путем подстановки результатов выполнения команды). Иногда требуется иное. Например, может возникнуть необходимость направить стандартный вывод команды на экран, а сообщения об ошибках (стандартный поток ошибок) собрать в файле с помощью обратных кавычек. Или же понадобится направить стандартный вывод в файл, а стандартный поток ошибок переадресовать команде обработки ошибок по каналу. Вот как решаются эти задачи в интерпретаторе Bourne shell. (В интерпретаторе С shell это невозможно.) Дескрипторы файлов О, I и 2 принадлежат стандартному вводу, стандартному выводу и стандартному потоку ошибок соответственно (объяснения — в параграфе 45.20). При отсутствии переадресации все они связаны с файлом терминала /dev/tty (45.20). Легко назначить любой дескриптор любому файлу, если известно имя файла. Например, чтобы переадресовать поток с дескриптором 2 в файл enfile, введите: $ команда 2>errfile Вы уже знаете, что символ канала и обратные кавычки позволяют переадресовать стандартный вывод: $ команда I ... $ переменная='команда' Однако с символами канала и обратными кавычками не связано никакое имя файла, поэтому для переадресации их потоков ошибок нельзя использовать оператор 2>. Для этого необходимо Программирование для подготовленных пользователей 751
45.21 переупорядочить дескрипторы файлов, даже если имена файлов, которым они соответствуют, неизвестны. Вот как это осуществляется. Оба стандартных потока, вывода и ошибок, направим в канал или оператору " '. Оператор п>&т переупорядочивает файлы и их дескрипторы. Этот оператор можно трактовать следующим образом: "Направить поток с дескриптором файла и в тот файл, в который направлен поток с дескриптором файла т". Используем этот оператор в предыдущем примере. Направим стандартный поток ошибок в то же место, куда направляется стандартный вывод: $ команда 2>61 | ... $ перененная="команда 2>61ч В обоих примерах 2>&1 означает: "Направить стандартный поток ошибок (дескриптор файла 2) в то же место, куда поступает стандартный вывод (дескриптор файла 1)". Очень просто, не так ли? Допускается использование нескольких операторов п>&ш. Интерпретатор shell читает их слева направо перед выполнением команды. "Эврика! — воскликнете вы. — Чтобы поменять местами стандартный вывод и стандартный поток ошибок, нужно заставить stderr идти в канал, a stdout — на экран. Это. же возможно!" $ команда 2>Sl 1>б2 | ... (неправильно...) Извините, дорогой читатель. Когда интерпретатор shell видит конструкцию 2>&1 1>&2, он сначала выполняет оператор 2>&1. Как вы уже могли убедиться, поток с дескриптором файла 2 (stderr) направляется в то же место, что и поток с дескриптором файла 1 (stdout). Затем интерпретатор shell выполняет оператор 1>&2. Он направляет поток stdout (l) в то же место, куда направлен stderr (2)... Но stderr уже направлен в то же место, что и stdout. в канал! Это один из тех случаев, когда оказываются полезными дескрипторы файлов с номерами от 3 до 9. Обычно они не используются. Один из них можно употреблять для обозначения "временного хранилища", т.е. для запоминания того, куда направлен поток с другим дескриптором файла. Оператор 3>&2 можно трактовать следующим образом: "Направить поток 3 в то же место, что и поток 2". Запомнив место назначения потока 2 с помощью оператора 3>&2, можно направить поток ошибок куда-нибудь еше. Затем направьте поток 1 туда, куда направлен поток 2 (и куда теперь направлен поток 3). Далее мы выполним все это по шагам. Необходимо использовать одну из следующих командных строк: $ команда 3>S2 2>Sl 1>63 | ... $ леременная='команда 3>s2 2>Sl 1>63' Как это работает? На следующих четырех рисунках командная строка (с обратными кавычками) анализируется в соответствии с теми действиями, которые выполняет интерпретатор shell в процессе переупорядочения дескрипторов файлов. Если хотите, можете проверить это на своем терминале. На каждом следующем рисунке добавляется еще один оператор n>&m и демонстрируется, куда направляется каждый поток после выполнения этого оператора. В примере, приведенном на рисунках, используется команда grep, читающая два файла. Файл afone доступен для чтения, и команда grep находит в нем подходящую строку, которая записывается в стандартный вывод. Файл bfoen указан неправильно, поэтому он недоступен для чтения, и команда grep записывает сообщение об ошибке в стандартный поток ошибок. На каждом рисунке показан вывод на терминал (если он есть) сразу после выполнения команды установки переменной с помощью оператора ' '. Текст, полученный посредством обратных кавычек, направляется в переменную интерпретатора shell. Команда echo отображает этот текст. На рис. 45.6 переадресация выполнена правильно. Стандартный вывод направляется на экран, а стандартный поток ошибок записывается в переменную. 752 Часть восьмая. Программирование в среде shell
45.21 $ vax='grap "Joe" afone bfoen' grep: bfoen: No such file or directory $ echo "$var" afone: Joe Jones 423-4567 Оператор'* F.D.I F.D.2 F.D.O F.D. —дескриптор файла /dev/tty ЪвШштШтшс:. Рис. 45.3. Дескрипторы файлов до переадресации $ var=*grep "Joe" afone bfoan 3>s2" grep: bfoen: No such file or directory $ echo "$var" afone: Joe Jones 423-4567 Оператор'' F.D. 1 F.D. 2, F.D. 3 F.D.O F.D. —дескриптор файле /dev/tty Рис. 45.4. Дескрипторы файлов поспе переадресации 3>&2 $ vai='grep "Joe" afone bfoen 3>S2 2>S1' $ echo "$var" afone: Joe Jones 423-4567 grep: bfoen: No such file or directory Оператор'* F.D. 1, F.D. 2 ■*{ F.D.3 F.D.O F.D.—дескриптор файла /dev/tty :шшш .V.V.V.'ik.tUV.VM.'iM.V.V Рис. 45.5. Дескрипторы файлов поспе переадресации 3>&2 2>&1 Программирование для подготовпенных попьзоватепеи 753
45.22 $ var='grep "Joe" afone bfoen 3>S2 2>sl I>s3' afone: Joe Jones 423-4567 $ echo "$var" grep: bfoen: No such file or directory Оператор'' F.D.2 /dev/tty F.D. 3, F.D. 1 F.D.0 F.D. — дескриптор файла Рис. 45.6. Дескрипторы файлов после лереацресации 3>&2 2>&1 1>&3 После завершения процессов открытые файлы автоматически закрываются. Однако безопаснее закрыть файлы самостоятельно по окончании работы с ними. Тогда, если вы присвоите существующий дескриптор другому файлу (например, используете дескриптор файла 3 для переадресации ввода-вывода другой команды или в порожденном процессе), конфликта не произойдет. Используйте оператор лк&-, чтобы закрыть файл ввода с дескриптором т, и оператор т>&-, чтобы закрыть файл вывода с дескриптором т. Для закрытия стандартного ввода используйте оператор <&-. Оператор >&- позволяет закрыть стандартный вывод. - JP 45.22 Построчная обработка файлов Трудно себе представить, как организовать построчное чтение файла в сценарии. И хотя можно дописывать файл по одной строке, сопровождая каждую команду дополнения файла оператором » (две правые угловые скобки), такого же эффективного способа построчного чтения не существует. Фокус в том, чтобы открыть файл и связать с ним дескриптор (3, 4,..., 9). UNIX хранит указатель файла наподобие закладки в книге. Этот указатель позволяет определить, в каком месте каждого открытого файла будет выполняться чтение или запись в следующий раз. Если, например, вы открыли файл для чтения и прочли первую строку, указатель будет установлен в начале второй строки. При следующем чтении из того же открытого файла указатель сместится в начало третьей строки. Этот прием применим только к тем файлам, которые остаются открытыми. Каждый раз при открытии файла его указатель перемещается в начало файла.* Команда exec (45.07) может открыть файл и связать с ним дескриптор файла. Например, следующая команда exec обеспечивает стандартный ввод всех последующих команд из файла formfile. ...все команды читают свой поток stdin с исходного места exec < formfile ...see команды читают свой поток stdin из файла formfile Существует другой способ переупорядочения дескрипторов файлов — в последней строке цикла while или конструкций if к case. Например, все команды в следующем цикле while получают свой стандартный ввод из файла formfile. Стандартный ввод за пределами цикла не меняется: .все команды читают свой поток stdin с исходного места while Оператор дополнения файла » устанавливает указатель в конце файла перед первой записью. 754 Часть восьмая. Программирование в среде shell
45.22 formprog do ...все команды читают свой поток stdin из файла formfile done < formfile . . .все команды читают свой поток stdin с исходного места Я называю такие циклы "циклами с переадресацией ввода-вывода". Эти и подобньге им структуры в интерпретаторе Bourne shell вызывают некоторые проблемы (45.23), однако полученный результат стоит времени, потраченного на их разрешение. Описанные приемы мы используем для заполнения бланков. Сценарий formprog читает файл пустого бланка, подобный следующему, строка за строкой: Name: Address: City: State/Province: Phone: FAX: Project: Corporate Decision Comments: Если строка содержит только метку, например Name:, сценарий предложит заполнить ее. Заполненную строку сценарий добавляет к выходному файлу, в противном случае строка в выходной файл не записывается. Если строка бланка уже заполнена: Project: Corporate Decision сценарий не предлагает заполнять ее, а просто записывает в выходной файл: % formprog formfile completed Name: Jerry Peek Address: 123 Craigie St. City: Cambridge State/Province: MA Phone: (617)456-7890 FAX: Project: Corporate Decision Comments: % cat completed Name: Jerry Peek Address: 123 Craigie St. City: Cambridge State/Province: MA Phone: (617)456-7890 Project: Corporate Decision Приводим текст сценария formprog. Номера строк указываются только для создания ссылок на фрагменты сценария. Не вводите их в файл. Дополнительные объяснения вы найдете после сценария. 1 #!/bin/sh 2 # formprog - заполнение шаблона бланка из $1; запись заполненного бланка в $2 3 # В ЭТОМ СЦЕНАРИИ УСТАНАВЛИВАЕТСЯ ШАГ ТАБУЛЯЦИИ 4 4 5 template="$l" completed="$2" errors=/tmp/formprog$$ 6 myname='basename $СГ # ИМЯ ЭТОГО СЦЕНАРИЯ 7 trap 'rm -f {errors; exit' 0 1 2 15 8 9 # ПОСТРОЧНОЕ ЧТЕНИЕ {template, ЗАПИСЬ ЗАПОЛНЕННЫХ СТРОК В {completed 10 exec 4<S0 # СОХРАНИТЬ ИСХОДНЫЙ stdin С ПОМОЩЬЮ ДЕСКРИПТОРА НОМЕР 4 11 while read label text 12 do Программирование для подготовленных попьэоватепей 755
45.22 13 case "$label" in 14 ?*:) # ПЕРВОЕ СЛОВО ЗАКАНЧИВАЕТСЯ ДВОЕТОЧИЕМ; СТРОКА ПОДХОДИТ 15 case "text" in 16 ?*) # ПОКАЗАТЬ СТРОКУ НА ЭКРАНЕ И ПОМЕСТИТЬ В ФАЙЛ completed: 17 echo "$label $text" 18 echo "$label $text" 1>&3 19 20 *) # ЗАПОЛНЕНИЕ СТРОКИ ПОЛЬЗОВАТЕЛЕМ X^£ 21 echo -n "$label " ^■'V 22 exec 5<S0 # СОХРАНИТЬ ДЕСКРИПТОР ФАЙЛА template; HE ЗАКРЫВАТЬ! ^^ 23 exec 0<&4 # ВОССТАНОВИТЬ ИСХОДНЫЙ stdin ДЛЯ ЧТЕНИЯ ОТВЕТА В ПЕРЕМЕННУЮ ans [46.10] 24 read ans 25 exec 0<&5 # ОПЯТЬ ПОДКЛЮЧИТЬ ФАЙЛ template К stdin 26 case "$ans" in 27 "") ;; # ПУСТО; НЕ ДЕЛАТЬ НИЧЕГО 28 *) echo "$label $ans" 1>S3 29 esac 30 31 esac 32 33 *) echo "$myname: bad $1 line: '$label $text'" 1>&2; break;; 34 esac 35 done <"$template" 2>$errors 3>"completed" 36 37 if [ -s $errors ]; then 38 /bin/cat $errors 1>&2 39 echo "$myname: should you remove '$completed' file?" 1>&2 40 fi В строке 10 использован оператор 4<&0 (4s.2i), предназначенный для сохранения исходного стандартного ввода (обычно — терминала, но не всегда)* как файла с дескриптором 4. (Нам нужно будет читать этот исходный поток stdin в строке 24.) В строках 11-35 цикла while с переадресацией ввода-вывода стандартный ввод всех команд поступает из файла $template, все стандартные потоки ошибок поступают в файл $errors, а все, что пишется в файл с дескриптором 3, дописывается в файл $completed. UNIX сохраняет указатели всех этих открытых файлов, поэтому каждая операция чтения-записи выполняется после строки, записанной или прочитанной последней. Вот что происходит при каждом выполнении цикла. 1. Команда read (44.13) в строке 11 читает следующую строку из своего стандартного ввода, т.е. из файла $ template. 2. Конструкция case (44.0S) в строках 15-31 проверяет текст из файла $ template. — Если строка наряду с меткой (заканчивающейся двоеточием) содержит и другой текст (сохраненный в $text), вся строка записывается по двум адресам. В строке 17 текст направляется на стандартный вывод, которым, вероятно, является экран (в любом случае он не переадресуется в сценарии). В строке 18 строка текста записывается в файл с дескриптором 3, т.е. в открытый файл $completed. — Если текст содержит только метку, в строке 21 она записывается в стандартный вывод (обычно — терминал) без символа новой строки. Мы хотим читать ответ в строке 24, но здесь существует проблема: в некоторых интерпретаторах Bourne shell команда read может читать только файл с дескриптором 0 и не позволяет использовать в командной строке такие операторы, как <&4. Мы не можем предполагать, что стандартный ввод всегда поступает с терминала, поскольку в этом случае невозможно было бы использовать сценарий formprog следующим образом: % лрогргшим_ганвратор_конанд | formprog % formprog < яоыандош4_файл 756 Часть восьмая. Программирование в среде shell
45.23 Поэтому в строке 22 мы сохраняем в дескрипторе файла 5 дескриптор открытого файла $template и местоположение указателя чтения-записи. В строке 23 файл стандартного ввода меняется, поэтому команда read в строке 24 читает данные, со стандартного устройства (с терминала). Строка 25 изменяет файл стандартного ввода, так что следующая команда read, находящаяся в начале цикла (строка 11), будет читать данные из файла $template. Если в строке 24 ответ не читается, в строке 27 ничего не происходит. Иначе в строке 28 строка текста записывается в файл с дескриптором 3, т.е. в открытый файл $completed. 3. Если метка шаблона не заканчивается двоеточием, в строке 33 записывается сообщение в поток stderr (дескриптор файла 2). Эти сообщения вместе с сообщениями, записанными в поток stderr другими командами цикла, переадресовываются в файл $errors. Если команда test (44.20), строка 37, обнаруживает какой-нибудь текст в этом файле, по окончании цикла он отображается в строке 38, а сценарий выводит предупреждение. Цикл продолжает чтение и запись строка за строкой, пока команда read, находящаяся в начале цикла, не обнаружит признак конца файла $ template. - JP 45.23 Ввод и вывод для циклов с переадресацией ввода-вывода Обычно интерпретатор Bourne shell запускает цикл с переадресацией ввода-вывода (45.22) в порожденном процессе (38.04). Для сценария formprog (см. параграф 45.22), кроме всего прочего, это означает следующее. • Любая команда в цикле, читающая свой стандартный ввод, будет читать данные из канала или из файла, переадресованного в стандартный ввод цикла. Это то, на что следует обращать внимание, поскольку единственной командой, которая должна читать данные из файла, обычно является команда read в начале цикла. Входные потоки других команд внутри цикла, например команд, считывающих информацию с терминала, должны быть переадресованы для чтения из других мест, а не со стандартного ввода цикла. • Во многих версиях интерпретатора Bourne shell при использовании команды ехЦ (зя.щ внутри цикла с переадресацией ввода-вывода завершается только порожденный интерпретатор shell, выполняющий цикл; сценарий при этом не завершается. Трудно назвать это "возможностью", я бы назвал это ошибкой. При выполнении сценария из параграфа 45.22 вы можете столкнуться с этой проблемой (см. следующий параграф). В более поздних версиях интерпретатора Bourne shell подобная ошибка исправлена, однако решение, приведенное ниже, должно работать во всех интерпретаторах Bourne shell. • Если внутри цикла содержится ошибка, из-за которой может прекратиться выполнение сценария, сообщение об ошибке записывается в файл с дескриптором 2, который переадресовывается в файл хранения ошибок порожденного интерпретатора shell. Оператор break позволяет корректно завершить цикл. Если по окончании выполнения цикла в файле ошибок что-то появится, то это значит, что была допущена ошибка, и сценарий может прекратить свою работу, даже если в нем остались команды. • Можно проверять код завершения (44.07) цикла с переадресацией ввода-вывода. Для выхода из цикла применяйте такие команды, как exit 0, exit 2 и т.д. Вне цикла сразу же после команды done используйте команду case $? (44.05) для проверки кода завершения. Например, код завершения 0 означает, что цикл выполнен нормально, код 1 является признаком ошибок одного вида, код 2 — другого и т.д. • Значение любой переменной интерпретатора shell или среды, изменяемое внутри цикла, вне цикла (после команды done в конце цикла) не изменятся. Вот как обычно решается эта проблема. Используется другой дескриптор файла, например 6, и в файл с этим дескриптором записываются команды установки переменных. Данный дескриптор назначается временному файлу. Затем посредством команды . (точка) (44.23) интерпретатора shell выполняется чтение этого временного файла вне цикла. Например, получить значение переменной varname вне цикла можно следующим образом: Программирование для подготовленных попьэоватепей 757
45.24 while условие do . . . echo "varname='значение'" 1>&6 done 6> var_set_file . var_set_file Грег Уббен прислал мне описание двух других способов, которым он отдает предпочтение. Для применения первого из них необходима команда read, принимающая в своей командной строке операторы переадресации, которые в данном случае выполняют большую часть задачи. Второй способ работает, если команды использования переменных можно разместить в пределах той же области видимости (между фигурными скобками озм)), что и операторы переадресации: exec 3< файл { while read line <s3 while read line do do уаг=эначение чаг^значение done done exec 3<&- echo "var = $var" echo "var - $var" } < файл Помещение цикла в функцию и переадресация ввода-вывода внутри функции, по-видимому, также дают возможность избежать упомянутой выше проблемы. Но не верьте на слово: проверяйте все в используемом вами интерпретаторе shell. - JP 45.24 Интерпретатор shell может читать сценарий со стандартного ввода, но... Вопрос. Какая разница между командами sh < файл и sh файл? Ответ. В первом случае в сценарии предотвращается чтение данных из его стандартного ввода. Рассмотрим сценарий zip'- while read word do echo $word I sed s/foo/bar/ done Если этот сценарий запускается как sh zip, то он читает ввод с терминала и меняет foo на bar. Если же запустить сценарий как sh < zip, он сразу же завершится из-за отсутствия ввода. — СТ, из телеконференции net.unix в Usenet, 29 декабря 1984 г. 45.25 Сценарии, получаемые "на лету" со стандартного ввода Интерпретатор shell может читать команды со стандартного ввода или из файла. Для получения ^^]£ последовательности, которая может включать те или иные команды, в зависимости от условий ШШ 7^ выполнения сценария, достаточно одной программы. Эта программа должна автоматически ^^ создавать командные строки и канал, по которому ее вывод передается интерпретатору shell, [45.24] выполняющему "автоматические" команды. Приведем пример.* Допустим, необходимо скопировать в один каталог файлы из другого каталога и всех его подкаталогов. Имена файлов в каталоге назначения не должны конфликтовать; не допускается совпадение имен файлов. Присваивать имена копиям файлов просто. Достаточно заменить каждую косую черту (/) в относительном путевом имени знаком Не рекомендуется применять этот прием в системах, в которых длина имени файла ограничена 14 символами. 758 Часть восьмая. Программирование в среде shell
45.26 "минус".* Например, файл /lib/glob/aprog.c будет скопирован в файл lib-glob-aprog.c. Для преобразования имен файлов можно использовать редактор sed (34.01), а для копирования — команду ср: ср каталОг_источник/1ib/glob/aprog.с каталог_приемник/lib-glob-aprog.с ср каталог__источник/lib/glob/аргод .h каталог_приемник /lib-glob-aprog .h Лучшее решение предполагает применение утилиты nawk (зз.п). В следующем примере использована команда find (n.oi) для получения списка путевых имен файлов (по одному на строку), содержащихся в каталоге copyfmm и его подкаталогах. Далее запускается команда nawk, предназначенная для создания имен файлов каталога назначения (например: ката- лог^приемник/lib-glob-aprog. с) и записи сгенерированных командных строк в стандартный вывод. Интерпретатор shell читает стандартный ввод из канала. Этот пример реализован в виде сценария, поскольку он довольно длинный, чтобы его набирать в командной строке. Однако при желании эти команды можно набрать и в интерактивном режиме: #!/bin/sh find copyfrom -type f -print I nawk '{ out = $0 gsubCV", "-", out) sub("Acopyfrom-","copyto/", out) print "cp", $0, out }' I sh Если вместо последней строки использовать sh -v (4t.oi), то эта опция интерпретатора shell обеспечит вывод каждой командной строки перед ее выполнением. Если последняя строка содержит sh -e, интерпретатор shell завершает работу сразу после того, как любая команда возвратит ненулевой код завершения (44.07). Это может произойти, например, если диск переполнен и команде ср не удается создать копию. - JP 45.26 Признак конца файла в конструкции "документ здесь" С признаком конца файла в конструкции "документ здесь" (8.18) связана одна неприятная проблема. Она заключается в том, что в интерпретаторах sh и csh действуют различные соглашения относительно обозначения этого признака. При использовании интерпретатора sh признак конца файла вводится непосредственно. Например: #! /bin/sh cat « 'eof Hi there, eof Если же вы работаете с интерпретатором csh, то должны предварить обозначение конца файла обратной косой чертой. Следующий сценарий выводит три строки, а не одну: #! /bin/csh cat «. \eof Hi. You might expect this to be the only line, but it's not. eof ■e'of \eof — CT, из телеконференции net.unix-wizards в Usenet, 20 июля 1984 г. * При замене косой черты таким символом, как [CTRL^a], также будут получены уникальные имена файлов, однако их труднее набирать. Программирование для подготовленных попьзоаатепей 759
45.27 45.27 Выключение эха при вводе "секретных" ответов При вводе пароля UNIX выключает эхо, вследствие чего вводимые символы не отображаются на экране. То же можно делать в сценариях с помощью команды stty -echo. #!/bin/sh trap 'stty echo; exit' 0 1 2 3 15 # используйте системную команду echo echo "Enter code name: \c" # echo -n "Enter code name: " stty -echo read ans stty echo Отве^ сохраняется в переменной ans. Команда trap (44.12) дает гарантию, что в случае нажатия пользователем клавиш [CTRL-c], предназначенных для остановки сценария, вывод символов на экран восстановится. - JP Краткий справочник по команде ехрг Команда ехрг— это очень удобный инструмент программирования. Она позволяет производить разнообразные операции сравнения, а также арифметические и логические операции. Команда ехрг является стандартной утилитой UNIX. Ее GNU-версия содержится на компакт-диске. Далее приведен синтаксис команды ехрг. Квадратные скобки служат признаком необязательного аргумента. Вводить их не нужно: ехрг аргумент! оператор аргумент2 [оператор аргументЗ ...] Аргументы и операторы должны быть разделены пробелами. Во многих случаях аргумент является целым числом, которое введено буквально или представлено переменной интерпретатора shell. Есть три типа операторов: арифметические, сравнения и логические. Код завершения (44.07) команды ехрг равен 0, если выражение не равно 0 или null; 1, если выражение равно 0 или null; и 2, если выражение ошибочно. Арифметические операторы Эти операторы используются для построения арифметических выражений и вывода результатов. + Сложить аргумент! и аргумент2 - Вычесть аргумент2 из аргумента! * Умножить аргумент! на аргумент2 /. Разделить аргумент! на аргумент2 % Вычислить остаток от деления аргумента 1 на аргумент2 Сложение и вычитание выполняются последними, если порядок операций не изменен с помощью скобок. Символы *, ( и) имеют особое назначение в интерпретаторе shell, поэтому их необходимо предварять обратной косой чертой или заключать в кавычки. Операторы сравнения Операторы данного типа употребляются для сравнения двух аргументов. Аргументами могут быть и слова. В таком случае предполагается, что a<z и A<z. Если выражение сравнения истинно, команда ехрг записывает в стандартный вывод (u.oi) единицу; если выражение ложно, то записывается 0. Перед символами > и < необходимо ставить обратную косую черту. Аргументы равны? ! = Аргументы не равны? > Аргумент! больше аргумента21 760 Часть восьмая. Программирование в среде shell stty 41.03 read 44.13 45.28 ехрг
45.29 >= Аргумент! больше или равен аргументу2? < Аргумент! меньше аргумента'2? <= Аргумент! меньше или равен аргументу2? Логические операторы Логические операторы используются для сравнения двух аргументов. В зависимости от значений аргументов, результат, записываемый в стандартный вывод, может равняться аргументу! (или некоторой его части), аргумеиту2 или 0. Символы | и & следует предварять обратной косой чертой. I Логическое ИЛИ. Если аргумент1 имеет ненулевое (и не пустое) значение, выводится аргумент!, иначе — аргумент2. & Логическое И. Если оба аргумента имеют ненулевое (и не пустое) значение, выводится аргумент!, иначе — 0. Напоминает команду grep (27.oi). Аргумент2 является шаблоном поиска в аргумен- те1. В этом случае аргумент2 должен быть регулярным выражением. Если шаблон аргумента2 заключен в символы \( \), то выводится совпадающая часть аргумента!, иначе выводится количество совпадающих символов. Шаблон всегда сравнивается с началом аргумента! (как при наличии символа л). Примеры Деление выполняется первым; результат равен 10: $ ехрг 5+10/2 Сложение выполняется первым; результат равен 7 (усеченное 7,5): $ ехрг \( 5 + 10 \) /2 Прибавление 1 к переменной /'; так инкрементируется переменная в сценариях интерпретатора Bourne shell: i='expr "$i" + 1' Вывод 1 ("истина"), если а является строкой hello: $ ехрг "$а" = hello Вывод 1 ("истина"), если Ь плюс 5 больше или равно 10: 5 ехрг "$Ь" +5 \>= 10 В приведенных ниже примерах переменная р является строкой version. 100. Эта команда возвращает количество символов в р: 5 ехрг "$р" : '.*' Выводится 11 Соответствие шаблону всех символов строки: 5 ехрг "$р" : '\(.*\)' Выводится "version.100" Вывод количества совпадающих строчных букв: 5 ехрг "$р" : '[a-z]*' Выводится 7 Вывод всех строчных букв: $ ехрг "$р" : '\([a-z]*\)' Выводится "version" Усечение значения ?х, если оно состоит более чем из пяти символов; иначе — вывод $х. (Логическое ИЛИ обеспечивает вывод второго аргумента, если первый равен 0 или пуст, т.е. когда совпадения нет.) 5 ехрг "$х" : '\( \)' \| "$х" — DG, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates i Программирование для подготовленных пользователей 761
45.29 45.29 Проверка символов в строке с помощью команды ехрг Команда ехрг (45.28) выполняет множество различных действий с выражениями. Одно из выражений, с которым она работает, содержит три аргумента: первый — строка, второй — двоеточие, третий — шаблон, заданный с помощью регулярного выражения (2б.щ. Строка и регулярное выражение обычно следует заключать в кавычки. Команда ехрг подсчитывает количество символов, соответствующих шаблону. Шаблон автоматически привязывается к началу строки, с которой он сравнивается, как если бы вы набирали в шаблоне признак начала строки (Л) при работе с командами grep, sed и т.д. Чтобы сохранить вывод команды ехрг в переменной, как правило, ее нужно запустить с помощью оператора ' ' (9.16): $ part="resistor 321-1234-00" name="Ellen Smith" $ ехрг "$part" : '[a-z]*[0-9]' —позиция первой цифры 10 $ len=expr "$name" : '[a-zA-Z]*'- $ echo first name has $len characters first name has 5 characters Когда некоторый символ (или символы) соответствует шаблону, команда ехрг возвращает код завершения (44.07) 0 ("истина"). Если вас интересует только проверка типа "истина"/"ложь", отбросьте числа, которые выводит команда ехрг, и проверяйте ее код завершения: /dev/null «.« 5 if ехрг "$part" : '.*[0-9]' > /dev/null > then echo \$part has a number in it. > else echo "it doesn't" > fi 5part has a number in it. - JP 45.30 Выделение частей строк Как анализировать (разбивать, искать) строку, чтобы найти последнее слово, второй столбец и т.п.? Существует множество способов. Выберите тот, который оптимально подходит вам, или же придумайте свой! (UNIX позволяет работать со строками различными способами.) Применение команды ехрг Команда ехрг (45.28) может выделять части строки с помощью регулярного выражения. Приведенный ниже пример взят из сценария, при вызове которого последним аргументом командной строки является имя файла. В следующих двух строках используется команда ехрг для выделения последнего аргумента и всех аргументов, кроме последнего. Параметр "$*" предоставляет команде ехрг список всех аргументов командной строки в одном слове. (Параметр "$@" (44.15) в данном случае не работал бы, поскольку он предоставляет отдельные аргументы в кавычках, а команде ехрг необходим список всех аргументов строки в одном слове.) last='expr ■'$*■■ : ■.* \{.*\)'~ # ПОСЛЕДНИЙ АРГУМЕНТ firsc=-expr "$*" : '\(.*\) .*'' # ВСЕ, КРОМЕ ПОСЛЕДНЕГО АРГУМЕНТА Рассмотрим регулярное выражение, предоставляющее последнее слово. Начальная часть выражения (.*) соответствует максимально возможному количеству символов, за которыми следует пробел. Начальная часть шаблона соответствует всем словам вплоть до последнего пробела. Остальная часть шаблона (\(.*\)) соответствует последнему слову. Регулярное выражение, выделяющее первые слова, похоже на предьщущее с той лишь разницей, что я изменил положение символов \( \). Теперь оно выделяет все слова до последнего пробела, но не включая его. Последняя часть регулярного выражения (.*) 762 Часть восьмая. Программирование в среде shell
4S.30 соответствует последнему пробелу и последнему слову, и команда ехрг игнорирует их. Поэтому на самом деле символы . * здесь не нужны (хотя пробел все же необходим). Я оставил эти символы, чтобы как можно меньше переделывать предыдущий пример. Команда ехрг очень удобна в случае необходимости разделить строку на две части. Выражение . * позволяет команде ехрг отлично справляться с пропуском переменного количества слов, когда количество слов в строке неизвестно заранее. Однако команда ехрг не в состоянии выделить, скажем, четвертое слово строки. Кроме того, она практически бесполезна, если работа ведется одновременно с несколькими строками текста. Применение команды echo с утилитами awk, colrm и cut Утилита awk позволяет разбить строку на слова. Однако эта утилита слишком громоздка, а для ее выполнения требуется много времени, особенно в загруженных системах. Команды cut (35.14) и colrm (35.15) запускаются быстрее, чем awk, и имеют не меньшие возможности. Все эти утилиты предназначены для обработки множества строк текста. Вы можете указать утилите awk обрабатывать единственную строку с помощью операторов сравнения с шаблоном и переменной NR. Можно также применять эти утилиты к одной строке текста, передаваемой в стандартный ввод по каналу от команды echo (в.ов). Вот как, например, можно получить третье поле из строки, разделенной двоеточиями: string ="this:is:just:a:dummy:string" £ield3_awk=~echo "$string" I awk -F: '{print $3}'~ £ield3_cut='echo "^string" I cut -d: -f3' Объединим две команды echo. Одна будет посылать текст команде awk, cut или colrm по каналу. Утилита проигнорирует весь текст в позициях 1—24, а затем выведет символы от 25-го до конца переменной text. Внешняя команда echo выведет The answer is и ответ. Обратите внимание, что внутренние двойные кавычки защищены с помощью обратной косой черты. Это предотвращает их интерпретацию в Bourne shell перед выполнением вложенной команды echo: echo "The answer is "echo \"$text\" | awk '(print substr ($0,25) } ' ' " echo "The answer is 'echo \"$text\" | cut -c25-'" echo "The answer is 'echo \"$text\" | colrm 1 24'" Применение команды set В интерпретаторе Boume shell команду set (44.19) можно использовать для разбивки отдельной строки и сохранения ее частей в параметрах командной строки (44.15) "$@", $*, $1, $2 и т.д. Затем при необходимости производится циклическая обработка слов с помощью конструкции for (44.16), а также реализуются другие возможности работы с аргументами командной строки, присущие интерпретатору shell. Можно также установить переменную IFS (3s.ii), которая задает символ-разделитель. Применение редактора sed Утилита sed (3424) в UNIX хороша для анализа ввода, который, может быть, нельзя разбить на слова другими средствами; для поиска и вывода единственной строки из группы строк текста, а также для решения многих других задач. В следующем примере необходимо вычислить процент заполнения файловой системы, смонтированной в каталоге /home. Данная информация спрятана в выводе команды d[ (2409). В моей системе этот вывод имеет следующий вид: % df Filesystem kbytes used avail capacity Mounted on /dev/sd3c 1294854 914230 251139 78% /work /dev/sd4c 597759 534123 3861 99% /home Мне нужно число 99 из строки, заканчивающейся словом /home. Шаблон / \/home$/ позволит найти эту строку (постановка пробела перед /home гарантирует, что шаблону не будет соответствовать строка, заканчивающаяся последовательностью типа /something/home). Программирование для подготовленных пользователей 763
45.31 Опция -п запрещает редактору sed выводить любые другие строки, кроме строки, которую мы просим вывести (с помощью команды р). Я знаю, что единственным.словом в строке, которое заканчивается знаком процента (%), является слово в столбце capacity. Пробел после первого шаблона . * обеспечивает, что шаблону не будет соответствовать первая цифра числа, совпадающая с шаблоном [0-9]. Скобки, предваренные обратной косой чертой (л.ю). позволяют выделить нужное число. Вот как это выглядит: usage='df I sed -n '/ \/home$/s/.* \ ([0-9] [0-9] *\}% . *Л1/р'' Сочетание команд sed и eval (S.w) позволяет присвоить сразу нескольким переменным значения, равные частям одной и той же строки. Приводим командную строку, которая присваивает двум переменным значения согласно выводу команды df: eval 'df I sed -n '/ \/home$/s/*(A ]* *\([0-9]*\) *\([0-9]*\) .*/kb=\l u=\2/p'' Эта команда содержит регулярное выражение, в котором применяются операторы скобок редакгора sed. С их помощью из вывода команды df выделяются столбцы kbytes и used. Эти два значения присваиваются переменным kb и и. По завершении работы редактора sed результирующая командная строка имеет следующий вид: eval kb=597759 u=534123 Теперь значение $kb равно 597759, a $u — 534123 - JP 45.31 Вложенная подстановка результатов выполнения команды В параграфе 9.16 мы рассмотрели процесс подстановки результатов выполнения команды с помощью пары обратных кавычек. Давайте возобновим в памяти полученные знания. Интерпретатор shell выполняет строку, расположенную между обратными кавычками, как команду, а затем заменяет эту строку выводом команды. Иногда, хотя и не очень часто, возникает необходимость использовать результаты одной команды в обратных кавычках в качестве аргументов другой команды, также окруженной обратными кавычками. Для этого следует вложить операторы ' ', сообщив таким образом интерпретатору shell, какую команду с обратными кавычками выполнять первой. Результат первой команды будет передан второй команде. Здесь возникают сложности. В интерпретаторе Кот shell предусмотрен более легкий способ, который мы продемонстрировали ниже. Приводим простой пример: в первой строке использован вложенный оператор подстановки результатов выполнения, а следующие две команды демонстрируют выполнение вложенных команд:* $ echo "Next year will be 19'expr \'date +%yV + 1'." Next year will be 1997. ■ S date +%y 96 $ expr 96+1 97 Команда, выполняемая первой, помещена в обратные кавычки (\ Л'). В предыдущем примере таковой является команда date +%y. Она выводит год (в данном случае — 96), и это значение .передается команде ехрг, которая складывает 96 и 1 и получает 97. Затем результат (от внешних операторов подстановки результатов выполнения) передается команде echo, которая и выводит сообщение. Почему внутренняя команда, заключенная в обратные кавычки (W), выполняется первой? Это происходит потому, что обратная косая черта перед обратной кавычкой отменяет ее специальное назначение (S.ы>. Итак, что же видит интерпретатор shell, когда впервые * Да, это не будет верным после 1998 г. Да, интерпретатор Кот shell имеет встроенную арифметику. Но зато это простой пример! 764 Часть восьмвя. Программирование в среде shell
45.32 анализирует командную строку (s.osft Он видит только обратные кавычки, окружающие команду ехрг, и запускает команду: expr 'date +%y" + 1 Однако, анализируя далее эту команду, интерпретатор shell видит в ней также обратные кавычки (на этот раз им уже не предшествует обратная косая черта) и запускает соответствующую команду — date +%у. Эта команда выводит 96. Далее shell выполняет команду ехрг 96 + 1, которая выводит 97. Теперь команда echo может вывести свое сообщение. Вот такие дела. Если вы применяете в работе интерпретатор Korn shell или bash, в вашем распоряжении есть более простое средство — оператор $ (команда). Оператор $( ставится перед командой, с которой можно было бы использовать открывающую обратную кавычку. Закрывающая скобка ставится после команды вместо закрывающей обратной кавычки. Вот как выглядит предыдущий пример с операторами $ ( ). За ним приведен более жизненный пример. $ echo "Next year will Ь* 19$(ехрг $(date +%y) + 1)." Next year will be 1997. 2>&1 45.21 5 tarout=$(tar cf /dev/rstl $(find . -type f -mtijne -1 -print) 2>tl) . . .спустя некоторое время... $ echo "$tarout" tar: ./files/145923: Permission denied Вложенная команда — в данном случае find (n.oi) — выполняется первой. Ее вывод (список имен файлов) помещается в командную строку tar (20.01). Наконец, вывод команды tar (сообщение об ошибке) сохраняется в переменной tarout интерпретатора shell. Новички (а также некоторые опытные профаммисты) могут подумать, что не стоит использовать вложенные подстановки, поскольку в них легко запутаться. Мне же кажется, что вложение вносит ясность. Оно более компактно и не требует временного сохранения результатов. Кроме того, его не так уж трудно понять, если вникнуть в то, что происходит. Еще один интересный пример приведен в параграфе 38.13. - JP Улучшенная команда read: grabchars Утилита grabchars получает символы, вводимые пользователем, не дожидаясь нажатия клавиши [RETURN]. Помимо прочего это позволяет писать сценарии с интерактивным интерфейсом. По умолчанию команда grabchars получает символ со стандартного ввода, отображает этот символ в стандартном выводе и возвращает код завершения (44.07) 1, свидетельствующий о том, что прочитан один символ. Опции (см. документацию) позволяют принимать более одного символа или только определенные символы, выводить запрос к пользователю и многое другое. Приведем пример. С помощью обычных команд echo (а.щ и read (44.13) запрос к пользователю можно вывести следующим образом: echo -n "Answer у or n, then press RETURN: " read ans При использовании команды grabchars запрос к пользователю может быть выведен в стандартный поток ошибок, а ответ пользователя — прочитан сразу после нажатия клавиш. Для переадресации стандартного потока вывода (ответа пользователя, выводимого командой grabchars) можно использовать обратные кавычки (9.пу. ans='grabchars -q1Answer y or n: ' * По умолчанию ответ, который читает и выводит команда grabchars, на стандартный вывод не направляется; пользователь не видит, что он набирает. Это прекрасно, если ответ нужно держать в секрете. Есть две возможности отображения ответа: Программирование для подготовленных попьзователей 765 45.32 ■■W' grabchars
45.33 Использование опции -Ь. В этом случае команда gmbchars выводит ответ как в stdout (переадресация которого осуществляется посредством обратных кавычек), так и в stderr (который обычно связан с терминалом). Вы можете воспользоваться моими любимыми трюками, дополняя ответ пользователя у него на глазах. Например, если пользователь вводит у, то сценарий выводит yes. Ответ п отображается как по. Любой другой ответ (х) отображается как х? Please answer у or п. Вот этот простой код, включающий цикл while (44.10), в котором запрос повторяется до тех пор, пока пользователь не ответит правильно: while : do ans=~grabchars -q'Answer у or n: ' ' case "Sans" in y) echo "yes" I>s2; break ;; n) echo "no" 1>&2; break ;; *) echo "5{ans)? Please answer у or n." 1>&2 ;; esac done Опция -справильные_символы сообщает программе grabchars, что нужно принимать только символы, перечисленные в параметре правильные_символы (это может быть регулярное выражение, например [a-z]). Если пользователь введет неподходящий символ, программа grabchars проигнорирует этот ответ и будет ждать следующего. Так, чтобы принимать только символ у или п, используйте: ans='grabchars -c'yn1 -q'Answer у or n: ' ' Существует целый ряд других опций. Мы хотели бы рассказать еще о двух из них. (По поводу остальных обратитесь к соответствующей документации.) С помощью опции -/для программы grabchars можно установить лимит времени. Если спустя заданное время от пользователя не поступил ответ, программа grabchars может завершиться, а также вывести ответ по умолчанию, заданный опцией -d. Опция -t позволяет писать сценарии, которые выдают пользователю подсказки. Они также предоставляют пользователю возможность отвечать на запрос, когда он не согласен со стандартным ответом. Например: ans="grabchars -t5 -d'y1 -q'To stop, type n within 5 seconds: '* Если пользователь не отвечает в течение 5 секунд, программа grabchars автоматически выдает ответ у. - JP, DS 45.33 Проверка двух строк с помощью одного оператора case Оператор case (44.05) интерпретатора shell имеет ряд преимуществ перед командой test (44.20). Например, он может выполнять сравнение с шаблоном. Однако команда test имеет опции -я и -о — операторы И и ИЛИ, которые непросто использовать с оператором case. С другой стороны, команда test не встроена в интерпретаторы shell некоторых старых версий. Предлагаем вашему вниманию способ проверки двух строковых переменных с помощью оператора case. Он не решит всех ваших проблем, однако если вы тщательно обдумаете, какие значения могут иметь проверяемые переменные, то задача будет решена. Между двумя строковыми переменными необходимо ставить разделитель. В следующем примере я выбрал в качестве разделителя косую черту (/). Кстати, допускается использование в этом качестве любого символа, который не применяется в шаблонах оператора case (44.06) и который не может содержаться в параметрах $# и $1. В настоящем примере оператор case проверяет аргументы командной строки сценария: case "$#/$l" in l/-f) redodb=yes ;; О/) ;; *) echo "Usage: $myname J-f] " 1>&2; exit 1 ;; esac • : 4S.09 1>&2 8.06 break 4S.09 766 Часть восьмая. Программирование а среде shell
45.35 Если в командной строке находится один аргумент (переменная $# равна 1) и он ($1) равен -/ то подходит первый шаблон и переменной redodb присваивается значение. Если аргументов нет, $# равен 0, а $1 остается пустым. В таком случае подходит второй шаблон. В остальных случаях количество аргументов задано неправильно. Тогда подходит третий шаблон. При этом сценарий выводит сообщение об ошибке и завершается. Конечно, этот способ позволяет решать гораздо более широкий круг задач, чем просто проверка аргументов командной строки. - JP 45.34 Массивы в интерпретаторе Bourne shell С shell (47.05), awk (зз.п), Korn shell и некоторые другие интерпретаторы shell в UNIX обладают встроенными средствами поддержки массивов. Стандартный интерпретатор Bourne shell не осуществляет такой поддержки, хотя его командная строка является чем-то вроде массива, который можно сохранять с помощью команды set (44.19) и к элементам которого можно обращаться с помощью параметров $1, $2 и т.д. Вы можете сохранять и использовать переменные интерпретатора Bourne shell с такими именами, как array 1, array2 и т.д., имитируя элементы массива I, 2 и т.д. Этот трюк реализуется посредством команды eval (s.to). В качестве совета: если в переменной п хранится индекс массива (1, 2 и т.д.), можно сохранять элементы массива part с помощью команды eval part$n="значение" а использовать их значения с помощью команды eval echo "The part is \$part$n." В случае применения последней команды необходимо обеспечить дополнительную защиту специальных символов, поскольку команда eval просматривает командную строку дважды. Действительно, при первом проходе интерпретатор shell находит значение $п, удаляет косую черту и оставляет командную строку в таком виде: echo "The part is $part5." При следующем проходе интерпретатор заменяет переменную partS ее значением. Для сохранения в таких "ненастоящих" элементах массива строки текста, содержащей несколько слов, команда set не подходит. А вот цикл for (44.п) позволяет справиться с этой задачей. Например, следующая конструкция служит для сохранения строки текста в переменной temp, а затем в "массиве" part. echo "Enter the line: \c" read temp n=0 for word in Stemp do expr 45.28 n= ~ expr 5n + Г eval part$n="$word" done Первое слово из переменной temp попадает в переменную part], второе — в part2 и т.д. - JP 45.35 Использование в сценарии управляющих символов Иногда в файле сценария необходимо использовать непечатаемые символы. Если их ввести непосредственно в файл, они будут невидимыми для принтера и на экране или, что еще хуже, вызовут проблемы при печати такого файла или при его выводе на экран. Программирование для подготовленных пользователей 767
4S.36 Одним из случаев, в которых может понадобиться запись управляющих символов в сценарий, является написание команд замены для редактора sed. Ведь при этом непонятно, какие разделители использовать, поскольку заменяемые строки могут содержать практически любые символы: sed "s/$something/$whoknows/" Так как в редакторе sed в качестве разделителей разрешено употреблять практически любые символы (34.07), вместо косой черты (/) лучше применять управляющий символ, например [CTRL-c]. Необходимость в использовании цепочек непечатаемых символов возникает и при управлении терминалом. В этом случае целесообразно использовать команды, создающие управляющие символы во время выполнения сценария, и сохранять их в переменных интерпретатора shell. Применение команды echo Если ваша версия команды echo (в.ов,4б.ю) интерпретирует в командной строке восьмеричные числа типа \001 как соответствующие ASCII-символы (s/.оз), то все просто. Как следует из таблицы соответствия восьмеричных кодов ASCII-символам, 001 — это [CTRL-a]. Вы можете сохранить вывод команды echo в переменной интерпретатора shell и использовать эту переменную, как только понадобится символ [CTRL-a]: " •»./« ca=~echo ' \001' ~ # символ [CTRL-a] sed "s$(ca)$something$(ca)$whoknows$(ca)" Применение команд tr и echo Если ваша команда echo не позволяет создавать управляющие символы, для этой цели можно использовать утилиту tr. Команда tr также понимает восьмеричные числа. Заставьте команду echo выводить несколько символов, а команду tr — переводить их в нужные управляющие символы. Например, создать цепочку из таких четырех символов, как [ESCAPE], [CTRL-a], [ и [CTRL-w], позволяет следующая команда: escsec='echo 'ea[w' | tr 'eaw' '\033\001\027'' Команда fr-читает из канала символы, посланные командой echo. Она переводит е в [ESCAPE] (восьмеричный код 033), а — в [CTRL-a] (восьмеричный код 001), a w — в [CTRL-w] (восьмеричный код 027). Левая скобка не заменяется, а выводится как есть. Сценарий script.tidy в параграфе 51.06 демонстрирует способ занесения нескольких управляющих символов в несколько переменных интерпретатора shell с помощью одной команды. Это эффективно, поскольку ограничивает количество необходимых порожденных процессов. Кроме того, удобным средством получения управляющих символов является команда^ (4s.ii), имеющаяся на компакт-диске. - JP 45.36 Файл блокировки в интерпретаторе shell В настоящем параграфе мы предлагаем вашему вниманию эффективный и переносимый способ создания файла блокировки с помощью сценария.* Этот способ интересен также и в плане того, как в UNIX используется команда umask (22.04) и как в этой системе можно изменить права доступа к файлам (22.02). Файл блокировки следует применять, когда одновременно может быть запущено несколько экземпляров программы и необходима гарантия, что только один из них сможет выполнять определенные действия (например, модифицировать файл, выводить данные на принтер и т.д.). Предположим, у нас есть сценарий edmaster, который редактирует файл конфигурации config. Чтобы удостовериться, что несколько пользователей не смогут одновременно модифицировать файл config, первый сценарий edmaster должен проверить, существует ли файл блокировки. Если этого файла нет, сценарий edmaster создает его и модифицирует файл config. По завершении * Эту идею предложил Грег Уббен. 768 Часть восьмая. Программирование в среде shell
45.36 редактирования сценарий удаляет файл блокировки. Если кто-то попытается запустить второй процесс edmaster, этот процесс обнаружит файл блокировки, созданный первым процессом edmaster, остановится и будет проверять каждые несколько секунд, не исчез ли файл блокировки. Как только первый процесс edmaster удалит файл блокировки, второй процесс edmaster сможет создать собственный такой файл и приступить к редактированию файла conftg. Приводим части сценария, в которых проверяется наличие файла блокировки, а также создается и (позже) удаляется этот файл: # Задать имя файла блокировки: myname='basename $0" LOCKFILE=/tmp/lock.$myname ft Выполнение цикла, пока не будет снята блокировка: until (umask 222; echo $$ >$LOCKFILE) 2>/dev/null # создание файла блокировки do # В сообщении указан владелец # файла блокировки и дата создания файла set х "Is -1 5LOCKFILE" echo "Waiting for user $4 (working since $7 58 $9)..." sleep 5 done # Делаем то, для чего нужен монопольный доступ rm -f $L0CKFILE # снять блокировку Если кто-то попытается запустить сценарий edconfig после того, как пользователь jpeek уже запустил его, новый пользователь увидит: % edconfig Waiting for user jpeek (working since Aug 23 14:05)... ...5■секунд спустя Waiting for user jpeek (working since Aug 23 14:05)... ...еще 5 секунд спустя... ...теперь jpeek закончил работать и новый пользователь может редактировать файл. Как это работает? Почти все команды содержатся в первой строке цикла. Команда umask 222 задает режим создания файлов, предназначенных только для чтения (права доступа г—г—г—). Поскольку эта команда выполняется в порожденном интерпретаторе shell (зям), она оказывает влияние только на файл блокировки, созданный в порожденном интерпретаторе. В остальной части сценария сохраняется обычное значение umask. Если файл блокировки уже существует (создан другим процессом), цикл выполняет команду sleep 5. Спустя 5 секунд предпринимается попытка создать файл блокировки. Если файл существует, он доступен только для чтения, поэтому команда echo $$ >L0CKFILE возвращает ненулевой код завершения. Этот код обеспечивает продолжение выполнения цикла. Как только другой процесс (который установил блокировку) удалит файл блокировки, команда echo в порожденном интерпретаторе shell запишет идентификатор этого процесса в файл блокировки, и цикл until прекратится. Однако если файл блокировки доступен только для чтения, то как он мог быть создан? Это другая интересная сторона данного приема. Значение umask относится только к уже созданному файлу; если файла не существует, его можно создать. Например, файл с кодом доступа 000 можно создать, набрав: $ (umask 666; echo hi > afile) 5 Is -1 afile 1 jpeek wheel 3 Aug 23 14:08 afile -JP 2> 4SJ1 /dev/null 13.14 set 44.1V Программирование для подготовленных пользователей 25 9-171 769
46 Отладка сценариев shell 46.01 Советы по отладке сценариев В некоторых версиях интерпретатора Bourne shell сообщения об ошибках являются малоинформативными и потому бесполезными. Например, интерпретатор может вывести такое сообщение: End of file unexpected (неожиданный конец файла). Мы хотим ознакомить вас с теми приемами, которые позволят получать более подробную информацию о происходящем. Использование опций -xv Начните свой сценарий со следующей строки: #!/bin/sh -xv (Если ваша UNIX-система не работает с оператором #!, используйте команду set -xv (44. iv). Опции -xv обеспечивают вывод информации о том, что происходит, когда интерпретатор shell читает сценарий. Строки сценария будут отображаться на экране сразу после их прочтения интерпретатором. Все команды, выполняемые интерпретатором shell, отображаются со знаком "плюс" впереди. Заметим, что интерпретатор shell сначала читает весь цикл (for, while и т.п.), а затем выполняет команды цикла. Если вы хотите запустить сценарий в режиме отладки, но не желаете вставлять опции отладки в файл сценария, рекомендуем явно запустить интерпретатор shell из командной строки и включить в нее упомянутые опции: % sh -xv файд_сценария Вывод при отладке имеет довольно большой объем и обычно занимает более одного экрана. Поэтому я пропускаю его через программу постраничного просмотра, например pg. Поскольку интерпретатор shell посылает свой отладочный вывод в стандартный поток ошибок, я объединяю оба потока — stdout и stderr (u.M). Использование программы постраничного просмотра имеет еще одно преимущество: чтобы остановить сценарий до его завершения, достаточно ввести команду выхода из этой программы (например, q). Когда программа постраничного просмотра завершает свою работу, UNIX может даже прервать выполнение сценария (в таком случае появляется сообщение Broken pipe (so.n) (разорванный канал)). Вы хотите сохранить отладочный вывод в файле и просматривать его на экране? Используйте команду tee (п.ту, чтобы "разветвить" потоки вывода и ошибок сценария и через канал направить вывод команды tee программе постраничного просмотра. Если сценарий работает медленно, целесообразно выполнять его в фоновом режиме. Переадресуйте стандартные потоки вывода и ошибок (ii.os) во временный файл (2/м). Используйте команду tail -/ (2S.H) для наблюдения за файлом регистрации. Чтобы заняться решением других задач, пока выполняется сценарий, остановите работу команды tail (с помощью клавиш [CTRL-c] или другой команды прерывания), выполните необходимые действия, после чего снова запустите команду tail -/для продолжения наблюдения. 770 Часть восьмая. Программирование в среде shell
46.02 Наконец, если сценарий в нормальном режиме записывает что-то в свой стандартный вывод, можете разделить нормальный и отладочный вывод на два файла (u.oi). Непарные операторы Если интерпретатор shell вывел сообщение End of file unexpected, найдите строку сценария, содержащую открывающую кавычку, для которой отсутствует закрывающая кавычка. Возможно, интерпретатор shell ищет ее, но не находит. То же происходит, если пропущена круглая или фигурная скобка. Преждевременный выход Получив сообщение End of file unexpected, поместите где-то посредине сценария такие две строки: echo "DEBUG: quitting early..." 1>&2 exit При следующем запуске сценария эти команды выведут сообщение и остановят выполнение сценария в том месте, где вы их поместили. Если вы больше не получили сообщение End of file unexpected, значит, ошибка находится где-то ниже строки exit. Переместите эти две строки ниже и снова запустите сценарий. (В противном случае сместите строки вверх...) Пропущенные или лишние операторы esac,;;, fi и т.п. Сообщение типа line 23: ;; unexpected означает, что перед строкой 23 находится незавершенный фрагмент кода. Может появиться также сообщение fi unexpected. Проверьте вложенные конструкции //и case, а также подобные им конструкции и убедитесь, что они заканчиваются корректно. Сброс номеров строк внутри циклов с переадресацией ввода-вывода Интерпретатор shell может выдать сообщение об ошибке, где упоминается первая строка (line 1) или указан другой небольшой номер строки, который кажется неправильным, поскольку ошибок в начале сценария обычно не бывает. Проверьте циклы и другие структуры с переадресацией ввода-вывода (4S.22). Некоторые версии интерпретатора Bourne shell запускают для выполнения таких циклов отдельный интерпретатор shell и начинают нумерацию строк сначала. - JP 46.02 Проблемы с защитой специальных символов? Подумайте, а затем запустите команду echo Вопрос: Почему я не могу заставить работать следующий сценарий: case $col2 in "income") awk '{if (52=='$col2') { /* В ЭТОЙ СТРОКЕ ОШИБКА */ /* Я НЕ МОГУ ЗАСТАВИТЬ awk РАСПОЗНАВАТЬ НИ '$со12', НИ '$2' */ } • $filel ;; Ответ: Как следует из приведенного фрагмента кода, команда awk должна сравнить $2 с "income". Если подумать над этим (или заменить команду awk командой echo), станет ясно, что команда awk получает следующее: (if($2==income) ( /* В ЭТОЙ СТРОКЕ ОШИБКА */ Что же делает в этом случае команда awk? Она сравнивает значение $2 с содержимым переменной income. Если эта переменная не установлена, значение сравнивается с нулем или пустой строкой. Во избежание ошибки необходимо использовать команду ( if ($2 == "income") ( Отладка сценариев shell 25* 771
46.03 которую можно получить следующим образом: case $cols in income) awk ' { if ($2 == '"Scol2"') { ...сценарий awk... } ) '■ sfilel;; Замена команд сценария командой echo является удобным приемом отладки. - СТ, из телеконференции net.unix в Usenet, / ноября 1986 г. 46.03 Вывод значений переменных в интерпретаторе Bourne shell Чтобы просмотреть значения, которые в сценарии присваиваются переменным, следует добавить цикл, где пользователь указывает имя переменной и получает ее значение (4S.19/. % cat myscript #!/bin/sh while echo "Pick a variable; just RETURN quits: \c" read var do case "$var" in ■"■) break ;; *) eval echo \$$var ;; esac done В цикле сначала выводится запрос Pick a variable: (Выберите переменную), а затем — имя переменной. При нажатии [ENTER] цикл прекращается, в противном случае на экране отображается значение переменной. Команда eval (t.io) просматривает командную строку echo дважды. Прием, описанный в данном параграфе, хорош не только для отладки. Его можно применить в любом сценарии при необходимости получить значение переменной путем ввода ее имени. - JP 46.04 Предотвращение синтаксических ошибок при сравнении чисел Команды test и / (квадратная скобка) (44.20) можно использовать для сравнения двух чисел. Если одно из сравниваемых значений является пустой или неопределенной переменной, возникнет ошибка. Например, в следующем примере пустая переменная num вызывает синтаксическую ошибку: - if [ "$num" -gt 0 ] then ... Чтобы избежать возникновения синтаксических ошибок, добавьте ведущий нуль: if [ "0$num" -gt 0 ] then . . . В этом случае, если переменная $num является пустой, то будет сравниваться 0 с 0. Если же переменная $num имеет значение 1, проверка даст положительный результат, потому что 01 больше, чем 0. - JP 772 Часть восьмая. Программирование в среде shell
46.06 46.05 Предотвращение синтаксических ошибок при сравнении строковых переменных При использовании команды test или [ (квадратная скобка) (44.20) для проверки строк может возникнуть ошибка, если значение переменной начинается с дефиса. Например: if [ "$var" = строка ] then . . . Если значение переменной $var начинается с -г, то команда test может решить, что вы хотите проверить доступность файла для чтения. Одно из наиболее простых решений этой проблемы, которое не всегда приводит к положительному результату (см. ниже), — поместить лишний символ в начало каждой из сравниваемых строк. Это значит, что первый аргумент никогда не будет начинаться с дефиса и не будет выглядеть как опция: if [ "X$var" = Хстрока ] then ... Этот трюк не работает, если вы хотите, чтобы проверка привела к отрицательному результату, когда переменная пуста или не определена. Следующая конструкция предназначена для работы с пустыми переменными: case "${var+Xl" in X) ...выполнить, если переменная определена *) ...выполнить, если переменная не определена esac Если переменная var определена (даже если она содержит пустую строку), интерпретатор shell меняет значение ${ уаг+Х1 (45.12) на X и выполняет первую часть конструкции case. Иначе используются команды, предназначенные для случая, когда переменная не определена. - JP 46.06 Ошибка при вызове интерпретатора Bourne shell с опцией -е Вызов интерпретатора Bourne shell с опцией -е должен приводить к остановке сценария, если команда возвращает ненулевой код завершения. Не кажется ли вам, что в результате использования опции -е некоторые команды //прекращают выполнение сценария? Если это так, то ваша копия интерпретатора Bourne shell является ошибочной. Такие копии распространялись с 4.2BSD, 4.3BSD и, возможно, с некоторыми другими системами. Обнаружить ошибочную версию позволяют команды $ sat -в $ if false; then echo yipe; else echo ok; fi которые приводят к выходу из интерпретатора shell, если он содержит ошибку. Кроме того, можно выполнить команды $ set -e || 44.09 $ false 11 echo ok ИЛИ $ set -e $ while false; do :; done что также приведет к выходу из интерпретатора shell. Чтобы исправить ошибку, возьмите исходный текст интерпретатора и внесите изменения в трех местах файла хес.с. Вам понадобится изучить язык Bournegol [похожий на ALGOL диалект С, который Стив Бурн использовал для написания исходной версии интерпретатора Отладка сценариев shell 773
46.07 Boume shell — JP\. Другой способ состоит в замене интерпретатора /bin/sh, поставляемого с системой, одним из бесплатных ^-подобных интерпретаторов (i.os). Перед выполнением команд, вызывающих ошибку, можно использовать команду set +e. К сожалению, некоторые ошибочные версии интерпретатора Boume shell не поддерживают даже этой команды. В таком случае единственным выходом является запуск порожденного интерпретатора shell (за.04) без опции -е. — СТ, из телеконференции сотр.unix.questions в Usenet, 20 февраля 1990 г. 46.07 Защита специальных символов и аргументы командной строки Вопрос: Мне нужно передать сценарию некоторые аргументы, содержащие много слов. Я думал, что заключив аргументы командной строки в кавычки (8.ы), мне удастся сгруппировать их. Однако складывается впечатление, что сценарий каким-то образом игнорирует кавычки. Вот простой пример: $ cat script for arg in $* do echo "Argument is $arg" done $ script '12 3' 4 Argument is 1 Argument is 2 Argument is 3 Argument is 4 Ответ: Так и должен работать параметр $*. При наличии двух аргументов этот параметр заменяется параметрами $1 $2 [а не "51" и "$2" — Щ. Поэтому цикл for выглядит так: for arg in 1 2 3 4 Обратите внимание, что кавычки исчезли. Вы хотели, чтобы интерпретатор shell видел следующее: for arg in '12 3' 4 Вы не получите строку такого вида, однако сможете получить нечто, что вполне вас устроит: "W 44.1S for arg in "$@" Параметр $@ заменяется выражением $1" "$2 Заключив этот параметр в кавычки, получаем: for arg in "$1" "$2" Защита специальных символов в интерпретаторах shell достаточно сложна. В интерпретаторе С shell используется, в принципе, правильная идея (переменные выделяются согласно спискам слов (47.05); argv является таким списком), но при ее реализации возникают такие неуклюжие конструкции, как foreach arg ($argv:q) # двоеточие q ?!? Для специального случая циклического перебора аргументов интерпретатор Boume shell предоставляет конструкцию for arg do [т.е. без фразы in список — JP\: for arg do echo "Argument is $arg" done 774 Часть восьмая. Программирование в среде shell
46.08 которая выводит: Argument is 1 2 3 Argument is 4 Параметр "$@" по-прежнему необходим для передачи списка аргументов другим программам. К сожалению, поскольку $@ меняется на 51" "$2...5л-1" "$л (где л — количество аргументов), при отсутствии аргументов "$@М" меняется на а пара кавычек дает один аргумент. [Многие поставщики UNIX рассматривали это как ошибку и изменили поведение интерпретатора таким образом, что пара кавычек не считается аргументом. — JP\ Лучшим решением этой задачи является использование, например, такой команды: % cat bin/okeeffe #! /bin/sh exec rsh okeeffe.berkeley.edu -1 torec $(l+"?@"} Конструкция ${1 + "$@"} означает "подставить параметр $1, но если он определен, указать вместо него параметр "$@"". [В этом нет необходимости при использовании упомянутых выше версий интерпретатора Bourne shell с "исправленной ошибкой". — JP\ Следовательно, если нет ни одного аргумента, мы получаем параметр $1 (который не содержит ничего), иначе получаем параметр "$@" (который заменяется так, как описано выше). Выражение ${переменная+другое_эначение} является одной из реализаций подстановки параметров (45.12) в интерпретаторе sh. Другим часто используемым выражением является ${ пере - менная-исходное_значение}, которое заменяется выражением $ переменная, но если переменная не определена, используется исходное__значение. Всю необходимую информацию по этому вопросу можно найти в руководстве по интерпретатору sh, которое стоит прочесть несколько раз, экспериментируя во время чтения. — СТ, из телеконференции comp.unix.questions в Usenet, 18 марта 1988 г. 46.08 Проверка встроенных команд Некоторые программисты со стажем склонны полагаться на "возможности", на которые не стоило бы полагаться. Вот одно из ошибочных предположений: если команда cd не выполняется успешно, происходит выход из интерпретатора Bourne shell. Эта особенность не документирована (насколько мне известно), и все равно программисты писали такие сценарии, как этот: cd $somedir rm -rf * В такой ситуации интерпретатор Кош shell ведет себя иначе. Если выполнение команды cd завершается неудачно, интерпретатор ksh выводит сообщение об ошибке и продолжает читать сценарий. Из этого проистекали проблемы, связанные с тем, что пользователи интерпретатора Кот shell получали сценарии, разработанные для Bourne shell! Если поведение команды вам не известно, лучше принять дополнительные меры предосторожности. Например, в предыдущем примере команда rm удаляет содержимое любого каталога, кроме $somedir. Следует всегда проверять код завершения встроенной команды и выходить, если она возвращает ненулевой код. Например, оператор | | (44.09) заставляет сценарий прекратить работу, если команда cd не работает: cd Ssomedir || exit rm -rf * Отладка сценариев shell 775
46.09 Несомненно, стоит потратить время на тщательное тестирование сценариев, выполняющих какую-то опасную операцию, а также на поиск мест, где они могут работать неправильно. В первую очередь это следует производить при запуске сценария в новой системе или в другом интерпретаторе shell: проверяйте код завершения встроенных команд. - JP 46.09 Если команда не возвращает код завершения, проверяйте сообщения об ошибках Команды UNIX при успешном выполнении должны возвращать нулевой код завершения (44.07), а в противном случае — ненулевой. Не все команды делают это. Вот, например, какую я нашел запись теста, который проводил еще в 1985 г. с командой touch (2i.07)\ $ touch /tmp touch: /tmp: can only touch regular files $ echo $? 0 $ touch -f /usr/sro/usr.bin/touch.с touch: /usr/src/usr.bin/touch.c: couldn't chmod: Not owner S echo $? 0 S touch -z touch: bad option -z $ echo $? 0 Поскольку код завершения всегда был равен 0, как если бы команда срабатывала успешно, мой сценарий не мог проверять его на наличие ошибки. Существует два способа выхода из этой ситуации.* Первый — написание собственной версии shell-сценария touch (можно назвать его mylouch), второй — размещение кода (см. ниже) в том месте сценария, где необходимо использовать команду touch: # ЗАПУСК КОМАНДЫ touch С АРГУМЕНТАМИ (ВКЛЮЧАЯ ЛЮБЫЕ ОПЦИИ). # ОБЪЕДИНЕНИЕ stderr С stdout И ПРОВЕРКА СООБЩЕНИЙ ОВ ОШИБКАХ: 2>&1 45.21 out=" /bin/touch "$@" 2>&1' case "$out" in "") exitstat=0;; *bad\ option) exitstat=l ; echo "Sout" 1>&2 *does\ not\ exist) exitstat=3 ; echo "$out" 1>&2 *can\ only\ touch\ regular files*) exitstat=4 ; echo "$out" 1>&2 *couldn\'t\ chmod) exitstat=5 ; echo "$out" 1>&2 *couldn\'t\ chmod\ back) exitstat=6 ; echo "Sout" 1>&2 *cannot\ touch) exitstat=7 ; echo "$out" 1>&2 *) exitstat=10; echo "$out" 1>&2 esac exit $exitstat С помощью этих команд обрабатываются многие сообщения об ошибках. Остальные сообщения дают код завершения 10. Можно упростить сценарий, чтобы он возвращал код 1 при любой ошибке. С другой стороны, можно дополнить сценарий так, чтобы он учитывал все возможные (или интересующие вас) ошибки. В параграфе 27.19 показано, как получить список большинства возможных сообщений об ошибках. Кроме того, вы увидите эти сообщения, совершая типичные ошибки, например задавая недоступные для чтения или записи файлы. Приведенный сценарий работает не в каждой системе. Запускаемая вами команда может направлять в стандартный вывод свои обычные сообщения, которые вы бы не хотели смешивать с сообщениями об ошибках. Хуже того, некоторые программы записывают в стандартный вывод сообщения об ошибках! И все же изучение этой методики может стать для вас неплохим началом в овладении искусством борьбы с некорректными командами UNIX. - JP Благодарю Ричарда Доути (Richard Doty) за эту идею и за фрагмент сценария, который приведен ниже. 776 Часть восьмая. Программирование в среде shelf
46.10 46.10 Переносимая команда echo Одним из огорчительных изменений в UNIX (по крайней мере, для меня) является появление новых версий команды echo (в.об) с возможностью интерпретации таких Escape-последовательностей, как \с и \007. Конечно, неплохо иметь такую возможность. Однако когда сценарий должен работать и в Berkeley-системах, и в System V UNIX, необходимо учитывать возможности этой команды в конкретной системе. (Пользователи интерпретатора bash имеют лучшее, хотя и менее переносимое средство — опции -е и -Е, описанные в конце параграфа.) При использовании изначальной версии команды echo мы писали: echo -n "Answer у for yes or n for no: " В случае работы с новой версией необходимо писать: echo "Answer у for yes or n for no: \c" Если же неправильно использовать неправильную команду echo, то получится запутанный вывод. :-) Ознакомившись с решениями Брюса Барнетта (Brace Barnett) и Лайама Куина (Liam R. Е. Quin), я переделал их по-своему. Я определяю переменную, которую использую вот так: Secho "Answer у for yes or n for no: ${nnl)" Известно ли вам заранее, в какой версии UNIX будет работать ваш сценарий? Если да, введите команды echo без символа новой строки, как в приведенном выше примере, и поместите в начало сценария следующие строки: # УДАЛИТЕ СИМВОЛ КОММЕНТАРИЯ ДЛЯ СВОЕЙ СИСТЕМЫ: echo="echo -n" nnl= ;; # BSD #echo="echo" nnl="\c" ;; # Sys V #echo="echo -n" nnl= PATH=/usr/bin:$PATH; export PATH # SunOS Эти строки позволят пользователю, инсталлирующему сценарий, установить правильную версию команды echo. Но если ваш сценарий применяется в различных UNIX-системах (например, с помощью сетевой файловой системы) или запускается в системе, в которой пользователи могут выбирать возможности как BSD, так и System V (например, SunOS), его нужно конфигурировать при каждом запуске: case ""echo 'x\c'"" in 'х\с') echo="echo -n" nnl= ;; # BSD x) echo="echo" nnl="\c" ;; # Sys V *) echo "SO quitting: Can't set up echo." 1>&2; exit 1 ;; esac В этом примере интерпретатор shell запускает текущую версию команды echo и проверяет ее вывод. Новые версии команды echo интерпретируют \с и выводят символ х (без последующего символа новой строки, что в данном случае не имеет значения). Команды echo в Berkeley-системах выводят \с буквально, что соответствует первому шаблону. Подобным образом можно обрабатывать другие Escape-последовательности и непечатаемые символы. Например, забавить сценарий присваивать, переменной esc значение [ESC] позволяет одна из следующих строк: 'echo...033' 4S.3S .esc='echo -n d. I tr "d" "\033"' # BSD esc="\033" # Sys V При использовании интерпретатора bash можно задать режим работы для команды echo. Команда echo -e всегда интерпретирует Escape-последовательности, предваренные обратной косой чертой. Команда echo -Е не интерпретирует символы косой черты. Опция -п (без символа новой строки) работает в обоих случаях. - JP Отладка сценариев shell 777
47 Программировать для С shell? Нет! 47.01 Почему нет? Многие примеры этой книги рассчитаны на интерпретатор С shell. При работе с командной строкой этот интерпретатор имеет явные преимущества перед Bourne shell, выраженные в виде псевдонимов, перечня ранее введенных команд и т.п. (В новом интерпретаторе Когп shell сочетаются возможности обоих указанных интерпретаторов, a bash обладает еще и дополнительными возможностями.) Однако для csh характерны существенные недостатки, когда дело касается программирования. Том Кристиансен пролил свет на эти недостатки в своем знаменитом трактате "Пагубность программирования для С shell", который мы включили в книгу в виде параграфа 47.02. Вас может раздражать тон этого параграфа. Но если сделать скодку на то, что он изначально был помещен в Usenet о.зз) и выдержан в стиле, который на профессиональном жаргоне именуется "наезд" (flame), высказывания Тома покажутся еще весьма деликатными. :-) Мы согласны с большей частью критических замечаний Тома и не делаем в этой книге никаких попыток научить программированию в С shell. Однако мы все же включили несколько параграфов справочного характера по конструкциям, которые использовались при рассмотрении файлов конфигурации С shell (2.02). Причина очевидна: эти файлы являются не чем иным, как программами, читаемыми при запуске интерпретатора. В частности, описывается синтаксис операторов i£ (47.03) и switch (47.06), объясняется, как создавать и использовать массивы (47.05) в интерпретаторе С shell (прекрасная возможность, отсутствующая, как мы полагаем :-), в интерпретаторе Bourne shell). (В эту главу можно было бы поместить также описание цикла foreach (9.ii), приведенное в другой главе.) Выражения (47.04) в интерпретаторе С shell (например, $?prompt) могут использоваться в конструкциях // switch и foreach. - TOR, IP 47.02 Пагубность программирования для С shell Решено: интерпретатор csh абсолютно не годится для программирования и его использование в этих целях следует запретить. Меня шокируют и приводят в уныние люди, которые пишут условные конструкции, инсталлируют сценарии и другие "поделки", работая с интерпретатором csh. Интерпретатор С shell подкупает тем, что его условные операторы напоминают операторы языка С. В связи с этим обычно выбирается путь наименьшего сопротивления и пишутся сценарии для csh. К сожалению, это гиблый номер, а программисты редко осознают это даже после того, как обнаруживают, что многие простые программы в csh либо громоздки, либо вообще не работают. Более того, известны случаи, когда длительное использование интерпретатора С shell являлось причиной непрофессионализма в работе с интерпретатором Bourne shell, что приводило к ошибкам в файлах /etc/re и .cronrc. Это довольно серьезная проблема, поскольку данные файлы необходимо писать именно на языке Bourne shell. 778 Часть восьмая. Программирование в среде shell
47.02 Дескрипторы файлов Наиболее распространенной проблемой в «^-программировании является отсутствие возможности манипулировать дескрипторами файлов. Все, что можно делать, — это перенаправлять потоки stdin и stdout или направлять поток ошибок stden в stdout. Интерпретаторы, совместимые с Bourne shell, предоставляют множество более интересных возможностей. Запись в файлы В интерпретаторе Bourne shell можно открыть или перенаправить потоки, связанные с любым дескриптором файла. Например: exec 2>errs.out означает, что поток stden поступит в файл ens.out. А что, если необходимо закрыть stden и оставить только stdout? Ну, это же совсем простая операция: /dtv/null I3.I4 команда 2>/dev/null Однако такая простота характерна только для интерпретатора Bourne shell. В С shell можно предпринять лишь жалкую попытку, например: /dev/tty 4S.20 (команда > /dev/tty) >& /dev/null Но кто сказал, что stdout направлен на терминал? Следовательно, попытка сделана неверно. Такую простейшую операцию невозможно осуществить в интерпретаторе С shell. С помощью следующих строк в «А-сценариях невозможно направить в поток stderr сообщения об ошибках от лица сценария. В интерпретаторе Bourne shell для этих целей предназначена следующая команда: echo "$0: cannot find $file" 1>&2 А в С shell нельзя направить поток stdout в stden, поэтому вы вынуждены писать всякие глупости типа: sh -с "echo '${01: cannot find $file' 1>&2" Чтение файлов Все, что вы имеете в интерпретаторе С shell, — это оператор $<, читающий строку из устройства tty. А что, если переадресовать stdin? Как бы вы не упорствовали, все равно в качестве входного потока получите tty, который не удается переадресовать. А вот команда read в интерпретаторе Bourne shell позволяет читать данные из потока stdin, который может быть переадресован. Это означает, что можно вводить такие команды: exec 3< filel exec 4< file2 Теперь можно читать данные из файла с дескриптором 3 и получать строки из файла filel (или из файла file2, если воспользоваться дескриптором 4). В современных интерпретаторах Bourne shell для этого достаточного следующих команд: read some__var 0<&3 read another_var 0<&4 В более старых версиях, где чтение можно было выполнять только из файла с дескриптором 0, приходилось хитрить: exec 5<&0 # сохранить указатель на прежний stdin exec 0<&3; read some^var exec 0<&4; read another_var exec 0<&5 # восстановить поток stdin Закрытие дескрипторов файлов В интерпретаторе Bourne shell ненужные дескрипторы файлов можно закрыть при помощи, например, оператора 2>&-, а это не то же самое, что переадресация потока в /dev/null. Программировать для С shell? Нет! 779
47.02 Испытанные комбинации Может быть, вы хотите передать поток stderr по каналу какой-либо команде и оставить только поток stdout? He очень сложная задача, не так ли? Я уже обращал ваше внимание на то, что в интерпретаторе С shell выполнить это невозможно. А в интерпретаторе Bourne shell вот что можно делать: $ exec 3>Sl; grep ууу ххх 2>Sl 1>ЬЗ 3>S- | sed s/file/foobar/ 1>S2 3>S- grep: xxx: No such foobar or directory Стандартный вывод не затрагивается. Дескрипторы файлов закрываются (3>&-) в том месте, где мы считаем нужным. Мы передаем редактору sed поток stderr, а затем снова связываем с этим потоком дескриптор 2. Рассмотрим следующий конвейер: А | В | С Нам нужно узнать код завершения команды С. Делается это просто: он равен значению переменной $? или $status (в csh). Но если мы захотим определить код завершения команды А, то нам это не удастся (я имею в виду С shell). В интерпретаторе Bourne shell данный код можно получить, хотя это потребует изобретательности. Рассмотрим пример, в котором нужно пропустить стандартный вывод команды dd через команду grep, чтобы избавиться от "мусора" — строк records in/out, но так, чтобы возвращался код завершения команды dd, а не grep: device=/dev/rmt8 dd_noise='Л[0-9]+ [0-9]+ records (in|out)$* exec 3>&1 status=~((dd if=$device ibs=64k 2>sl 1>&3 3>s- 4>s-; echo $? >&4) | egrep -v "$dd_noise" 1>&2 3>«-.4>fi-) 4>Sl~ exit $status; Несовместимость команд Встроенные команды Встроенные команды в интерпретаторе csh — скопище недоделок. Их никак не удается совместить. Даже объединение таких команд, как % time I echo не должно вызывать подобных сообщений: Reset tty pgrp from 9341 to 26678 Бывают еще более забавные случаи: % sleep 1 | while while: too few arguments. [5] 9402 % jobs [5] 9402 Done sleep | Некоторые.команды могут даже "подвесить" интерпретатор csh. Попробуйте нажать [CTRL-z] при выполнении команды source или переадресовать вывод этой команды. Только прежде убедитесь, что в вашем распоряжении есть другое окно или терминал. Управление ходом выполнения программ Нельзя смешивать операторы, которые предназначены для управления ходом выполнения программ, и команды: who I while read line; do echo "gotta $line" done 780 Часть восьмая. Программирование в среде shell
47.02 Нельзя также создавать в csh многострочные каналы с помощью точек с запятой. Не существует простого способа определения псевдонима, аналогичного следующему: alias cmd 'if (foo) then bar; else snark; endif Нелепые ошибки при анализе командной строки Некоторые команды просто не работают, например: % kill -1 'cat foo' 'cat foo': Ambiguous. А вот с этой все в порядке: % /bin/kill -1 'cat foo' Остановленное задание [2] Stopped rlogin globhost ■можно.было бы завершить, казалось, с помощью команды % kill %?glob kill: No match А вот команда % fg %?glob работает. Может иметь значение наличие пробела. Так, команда if(expr) не работает в некоторых версиях интерпретатора csh, в то время как команда if (expr) работает! Сигналы Единственная возможность обработки сигналов в интерпретаторе С shell — перехват сигнала INT. В интерпретаторе Bourne shell можно перехватывать любой сигнал, а также такое событие, как выход из программы. Например, чтобы удалить временный файл по любому из множества сигналов, выполните следующее: trap 'rm -f /usr/adm/tmp/i$$ ; echo "ERROR: abnormal exit"; exit' 1 2 3 15 trap 'rm tmp.$$' 0 # при выходе из программы Защита специальных символов В интерпретаторе csh нет приемлемого способа защиты специальных символов, а команда set foo = "Bill asked, V'How's tricks?\"" ие. работает. Это существенно затрудняет (io.os) формирование строк со специальными символами. В интерпретаторе Bourne shell такой проблемы ие существует. На практике корректно функционирует даже такая команда: cd /mnt; /usr/ucb/finger -m -s 'Is \'u\" Знаки доллара в csh, увы, нельзя защитить с помощью двойных кавычек. % set foo = "this is a \$dollar quoted and is $HOME not quoted" dollar: Undefined variable. Программировать для С shell? Нет! 781
47.02 Для вставки символов новой строки необходимо использовать символы обратной косой черты, которые иногда чрезвычайно трудно вставить. % set foo = "this \ and that"; % echo $foo this and that % echo "$foo" Unmatched ". He совпадает что? Для Bourne shell подобные проблемы не характерны, в нем можно написать многострочные команды такого типа: echo 'This is some text that contains several newlines.' Синтаксис определения переменных Между глобальными и локальными переменными (переменными среды и переменными интерпретатора shell) есть большая разница. В интерпретаторе csh для определения переменных этих двух видов используется разный синтаксис. В интерпретаторе Bourne shell запись VAR=3HayeHHe команда аргументы идентична записи (export VAR; \'АР,=значение; команда аргументы) а в csh она означает: (setenv VAR; команда аргументы) Невозможно применить к переменным среды операторы :t, :h и т.п. <9.об). Смотрите, что получится при вводе следующей команды: % echo Try testing with $SHELL:t Try testing with /bin/csh:t БЫЛО бы удобно ИСПОЛЬЗОВаТЬ КОНСТРУКЦИЮ $ { PAGER-mo re } ИЛИ FOO (BAR:-${BAZ)} (45.12), чтобы запускать программу постраничного просмотра, если она указана в переменной PAGER, или программу more в противном случае. Но в интерпретаторе csh сделать это невозможно. Приходится быть более многословным. В С shell нельзя получить номер процесса последней фоновой команды, что желательно при запуске нескольких фоновых заданий. В Bourne shell идентификатор процесса последней команды, запущенной в фоновом режиме, содержится в переменной $!. Интерпретатор csh "капризен" также при импортировании переменных среды HOME, USER, РАТНи TERM в локальные переменные интерпретатора shell. Попробуйте выполнить команды % setenv TERM ''/bin/Is -1 / > /dev/tty'' % csh -f и вам станет весело! Вычисление выражений Рассмотрим следующее выражение в интерпретаторе csh: if ($?MANPAGER) setenv PAGER $MANPAGER Несмотря на ваши попытки определить только переменную PAGER, интерпретатор csh прекращает выполнение команды: MANPAGER: Undefined variable. неопределенная переменная 782 Часть восьмая. Программирование а среде shell
47.02 Это вызвано тем, что в любом случае интерпретатор анализирует всю строку и определяет ее значение! Поэтому данное выражение должно иметь такой вид: if ($?MANPAGER) then setenv PAGER $MANPAGER endif Та же проблема возникает в процессе выполнения следующей команды: % if ($?Х && $Х = 'foo') echo Ok X: Undefined variable. В связи с этим приходится создавать пару вложенных конструкций // что неэффективно для решения такой простой задачи. Если бы синтаксис интерпретатора csh действительно был похож на синтаксис языка С, можно было бы рассчитывать на свободное использование подобных логических выражений. Рассмотрим типичную конструкцию языка С: if (р && p->member) Неопределенные переменные не вызывают фатальных ошибок в интерпретаторе Bourne shell, поэтому описанная ситуация в данном случае не возникает. Несмотря на то что интерпретатор csh имеет встроенные возможности обработки выражений, они во многом не удовлетворят программистов. На самом деле интерпретация выражений зависит от наличия пробелов. Следующая строка содержит ошибку: @ а=4/2 а в этой — все в порядке: И а-4 / 2 Обработка ошибок Неплохо было бы узнать, есть ли в сценарии ошибки, не запуская его. Именно для этого предназначена опция -«, принцип действия которой — только проверка синтаксиса. Это особенно удобно, если необходимо проверить отдельные сегменты кода. Увы, реализация этой возможности в интерпретаторе csh оказалась неудачной, так что воспользоваться ею не удастся. Рассмотрим следующее выражение: exit (i) Конечно, на самом деле имелось в виду exit (1) ИЛИ exit 1 Любой интерпретатор shell не воспримет такое выражение, однако если его "спрятать" в конструкции if $!/bin/sh -fn if (1) then exit (i) endif интерпретатор С shell сообщит, что в сценарии нет ничего подозрительного. Идентичная конструкция в интерпретаторе Bourne shell при выполнении выдаст сообщение: #!/bin/sh -n if (1) then exit (i) endif /tmp/x: syntax error at line 3: '(' unexpected синтаксическая ошибка в строке 3: непредвиденный символ 'С Программировать дпя С shell? Нет1 783
47:03 Случайные ошибки В результате выполнения некоторых команд происходят непонятные вещи. Например, при вводе следующей команды создается файл дампа: !%s%x%s Использование в псевдонимах обратных кавычек (~ ), взятых в другие обратные кавычки, тоже приводит к созданию файла дампа. Попробуйте выполнить следующую команду: % repeat 3 echo "/vmu*" /vmu* ■ , . /vmunix /vmunix Что-что? Несмотря на то что некоторые поставщики интерпретатора csh устранили кое-какиё ошибки (в этом отношении интерпретатор tcsh (8.оз) гораздо лучше), ряд проблем невозможно разрешить, поскольку они являются следствием ошибочных архитектурных решений. Будьте благоразумны. Если уж нужно написать сценарий, то лучше писать его для интерпретатора Bourne shell. -ТС 47.03 Условные конструкции с оператором if В этом параграфе описан синтаксис конструкций с оператором if В параграфе 47.04 рассматривается синтаксис выражений, которые можно проверять в конструкции if Оператор #"используется в начале условной конструкции. Простейший формат этой конструкции следующий: if (выражение) команда л ■ Наряду с указанным существует еще три формата:. if -(выражение) then if (выражение)- then ' if (выражение1) then команды команды! команды! endif else else if (выра.жение2) then коыанды2. команды2 endif else командыЗ endif В простейшей конструкции команда выполняется, если выражение истинно, иначе никакого действия не производится (переадресация все же осуществляется, что является ошибкой). В других конструкциях выполняется одна или несколько команд. Если выражение истинно, активизируются команды, которые следуют за оператором then. Если же выражение ложно, происходит переход к командам после оператора else (или после оператора else if с проверкой еще одного выражения). Например, следующая конструкция if выполняет стандартное действие, если не задан ни один аргумент командной строки: if ($#argv == 0) then echo "No filename given. Sending to Report." set outfile = Report else set outfile = $argv[l] endif Дополнительные примеры вы найдете в параграфе 47.04. — DG, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates 784 Часть восьмая. Программирование в среде shell
47.04 ${переменная) $ {переменная [ i.] ) 47.04 _ Переменные, операторы и выражения в интерпретаторе С shell Переменные В следующих операторах подстановки фигурные скобки не обязательны. Исключение составляют случаи, когда необходимо отделить имя переменной от следующих символов, которые иначе были бы восприняты как часть имени. Массив argv (содержащий аргументы командной строки) используется в качестве примера. В интерпретаторе csh можно использовать любое другое имя массива. Значение переменной. Выбор слова или слов в позиции / переменной. Параметр i может быть числом, диапазоном чисел m-n, диапазоном чисел -л (вместо пропущенного m подразумевается 1), диапазоном чисел -т (вместо пропущенного л подразумеваются все оставшиеся слова). Параметр / может быть также переменной, заменяемой одним из указанных значений. Количество слов в переменной. Количество аргументов командной строки. Отдельные аргументы командной строки. Параметр « является числом (1, 12 и т.д.). То же, что и ${argv[n]}. Все аргументы командной строки. То же, что и ${argv[*] }. Последний аргумент. Возвращает 1, если переменная определена, и 0, если не определена. Возвращает 0, если переменная определена, и 1, если не определена. Номер процесса, в котором выполняется текущий интерпретатор shell. Используется как часть имени файла для создания временных файлов с уникальными именами. Чтение строки из стандартного ввода. ${# переменная) ${#argv} ${argv[n]} ${[n]} $Urgv[*]} .$* ${argv[$#argv]} ${?переменная) ! ${?переменная) $< Выражения Выражения используются в конструкциях @, if и while интерпретатора С shell для выполнения арифметических действий, сравнения строк, проверки файлов и т.д. Выражения можно употреблять и в командах exit и set. Выражения формируются путем комбинирования переменных и констант с операторами, напоминающими операторы языка С. Порядок выполнения операторов тот же, что и в С. Его можно представить следующим образом: 1. •/■%■ 2..+ - Выражения группируются с помощью круглых скобок. Скобки необходимы также, если выражение содержит оператор <, >, & или |. Операторы Каждый оператор принадлежит к одному из перечисленных ниже типов. Операторы присваивания Присвоить значение. += -= Присвоить новое значение после выполнения сложения или вычитания. *= /= %= Присвоить новое значение после умножения, деления или определения остатка при делении на заданное число. Программиршгь для С shell? Нет1 785
47.04 &= л= | = Присвоить новое значение после выполнения побитовых операций AND, XOR, OR. ++ Инкрементирование (увеличение на 1). Декрементирование (уменьшение на 1). Арифметические операторы * I % Умножение, целочисленное деление, деление по модулю (определение остатка при делении). + - Сложение, вычитание. Побитовые и логические операторы Двоичная инверсия (дополнение до единицы). ! Логическое отрицание. « » Сдвиг битов влево и вправо. & Побитовая операция AND (И). А Побитовая операция XOR. (исключающее ИЛИ). | Побитовая операция OR (ИЛИ). && Логическое AND (И). I | Логическое OR (ИЛИ). { команда } Возвращает 1, если команда выполнена успешно, иначе — 0. Обратите внимание, что это противоположно обычному коду завершения команды. Использование переменной status может оказаться предпочтительным. Операторы сравнения == != Равенство, неравенство. <= >= Меньше или равно, больше или равно. < > Меньше, больше. =~ Строка слева соответствует шаблону имени файла справа, содержащему *, ? или [...]. ! ~ Строка слева не соответствует шаблону имени файла справа, содержащему *, ? или [...]. Операторы запроса информации о файлах Перед запросом вьтолняется замена специальных символов в именах файлов. -d файл Файл является каталогом. -е файл Файл существует. -f файл Обычный файл. -о файл Файл принадлежит пользователю, выполняющему проверку. -г файл Пользователь имеет право на чтение данного файла. -w файл Пользователь имеет право на запись в данный файл. -х файл Пользователь имеет право на выполнение данного файла. -z файл Файл имеет нулевой размер. ! Изменение смысла предыдущих запросов на противоположный. Примеры Следующие примеры иллюстрируют использование команды @; предполагается, что я равно 4. Выражение Значение $х @ х = ($п > 10 I | $п < 5) 1 @ х = ($п >= 0 && $п < 3) 0 786 Часть восьмая. Программирование в среде shell
47.05 Выражение Значение $х @ х = ($п « 2) 16 @ х = ($п. » 2) 1 @ х = $п % 2 О @ х = $п % 3 1 В следующих примерах показаны первые строки конструкций if и while. Выражение Что означает while ($#argv != 0) Пока есть аргументы командной строки (argv) ... if ($today [1] == Fri) Если первое слово Fri ... if ($file !~ *. [sZ]) Если имя файла не заканчивается на х или .Z ... if ($argv[l] =~ chap?) Если первым аргументом является строка chap с одним последующим символом ... if (-f $argv[l]) Если первым аргументом является имя обычного файла ... if (! -d $tmpdir) Если tmpdir не является каталогом ... — DG, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates 47.05 Использование массивов интерпретатора С shell csh init Интерпретатор С shell может рассматривать свои переменные как списки слой. Я называю их массивами (5зм) потому, что они очень похожи на массивы в других языках программирования. Например, переменные path (6.os>, cdpath (ы.озу и mail (Н.щ интерпретатора С shell являются массивами. Между прочим, массивы прекрасно подходят для хранения информации в файлах конфигурации интерпретатора shell (2.02). При создании массива в скобки заключается набор значений, присваиваемый элементам массива. Между значениями массива ставится пробел. Внутри скобок допускается использование одинарных кавычек, символов обратной косой черты, двойных кавычек и т.д. Вот как можно поместить в массив job строку Fix the report в качестве первого элемента и слово resign в качестве второго: % set job=("Fix the report" resign) Знак доллара, предшествующий имени переменной интерпретатора shell, используется для получения ее значения. Это касается и всех элементов массива, поскольку он хранится как переменная интерпретатора shell. Чтобы выбрать отдельный элемент массива, укажите его номер в квадратных скобках после имени массива. Например: % echo $job Fix the report resign % echo $job[l] Fix the report Как и команда shift (44.П) интерпретатора Bourne shell, идентичная команда интерпретатора С shell изменяет нумерацию аргументов командной строки. Она также сдвигает элементы массива: % shift job % echo $job[l] resign Том Кристиансен рассказывал мне, что стек каталогов (м.ов) очень удобно помещать в массив. И он прав. Можно создать псевдонимы ро.02) для команд pushd и popd, которые сохраняют вывод команды dirs в массиве с таким же именем: alias pushd 'pushd \!* &£ set dirs=('dirs")' alias popd 'popd \\* && set dirs=('dirs')' Программировать для С shell? Нет! 787
47.06 В этом случае для просмотра третьего каталога стека можно использовать команду Is $dirs[3]. Кроме того, вы можете воспользоваться циклом foreach (ч.н), чтобы по очереди перебрать все элементы массива. Допустим, если требуется найти файл frobozz, находящийся в одном из каталогов стека, используйте оператор проверки -е (47.04), предназначенный для поиска существующего файла: % foreach <Цг ($dirs) > 9.13 ? if (-a $dir/frobozz) echo "frobozz ia in $dir" ? end frobozz is in /work/vol3/ch02.files/summaries % - JP 47.06 Оператор switch интерпретатора С shell: краткое описание Условная конструкция с оператором switch используется для обработки команд' с учетом значения какой-либо переменной. Когда, в зависимости от значения переменной.могут быть выбраны три и более варианта, конструкция switch является очень удобной альтернативой конструкции if-then-else. . Если переменная строка соответствует шаблону!, выполняется первый набор команд, если же переменная строка соответствует шаблону2, выполняется второй набор команд и т.д. Если ни один шаблон не подходит, выполняются команды после оператора default:. Параметр строка может быть определен путем подстановки результатов выполнения команды (9.16), подстановки значений переменных («.юр или ■: подстановки -имен файлов (i.i6>. При задании шаблонов допускается использование метасимволов-^,-? и []'.'_ Оператор breaksw предназначен для выхода из конструкции switch после выполнения набора команд. Если опустить этот оператор (что делается довольно редко), будет продолжено выполнение Следующего набора команд, цока не встретится оператор breaksw mm. endsw... Ниже схематически представлен синтаксис конструкции switch, а рядом — пример, в котором обрабатывается первый аргумент командной строки. switch (строка) case шаблон! : команды! breaksw case шаблон2: кома.нды2 breaksw case шаблонЗ: командыЗ breaksw . default: команды breaksw switch ($argv[l]) case -[nN]: nroff $file I lp breaksw case - [Pp] : pr $file I lp breaksw case -[Mm]: more $file breaksw case -[Ss] : sort $file breaksw default: echo "Error—no such option" exit 1 breaksw endsw endsw — DG, из книги UNIX in a Nutshell (SVR4/Solaris) издательства O'Reilly & Associates 788 Часть восьмая. Программирование в среде shell
Часть девятая Разное Мы подошли к концу, и в настоящей части представлен материал, не вошедший в предыдущие главы книги. Однако это не значит, что здесь собрана второстепенная информация. На самом деле вашему вниманию будут предложены некоторые из лучших программ нашей коллекции, в том числе программа для работы с электронными таблицами, пакет деловой графики, а также программа создания календаря в формате PostScript, познакомившись с которой, вы удивитесь, как можно было обходиться без нее раньше. Здесь также представлена информация о системе интерактивной документации по UNIX и сообщениях об ошибках, коллекция разнообразных полезных программ и находок, а также словарь терминов. В одной из глав приводятся инструкции по инсталляции программ с прилагаемого к книге компакт-диска. - TOR
48 Автоматизация офиса 48.01 0 чем пойдет речь в этой глзве Если говорить честно, в параграфах этой главы речь на самом деле идет не об автоматизации офиса: будут затронуты вопросы, не имеющие прямого отношения к офисной деятельности, но действительно интересные для работающих в офисе. Мы поговорим о следующем: • поддержке компьютерной базы данных телефонов и адресов («ту, • использовании терминального окна в качестве буфера промежуточного хранения («.ту, • использовании календаря (4S.M) с целью напоминания о назначенных встречах и даже для предупреждения об окончании рабочего дня («.озу, • выводе календарей на любой месяц (параграфы 48.06—48.08), в том числе шуточных, напоминающих о всех назначенных встречах (параграф 48.09); • поддержке простых баз данных с помощью программ awk («.щ и index (4S.n,4S.i2). Если вас интересуют программы для офиса, прочтите о программах sc (49.os) и ipl (49.09) в следующей главе. - TOR 48.02 Интерактивные списки телефонов и адресов phone Рассмотрим простой и полезный сценарий, используемый в издательстве O'Reilly & Associates. Вызвав его под именем phone, вы получите список телефонных номеров — сценарий ишет в вашем начальном и системном каталогах файлы с именем phone. Будучи вызванным под именем address, сценарий начнет искать файлы с именем address. Записи из системного файла получают метку sys>, а записи из персонального файла — метку pers>. Например: % phone torn pers>Tom VW's mother, Barbara Van Winkel in Vermont 802-842-1212 pers>Tom Christiansen [5/10/92] 201/555-1212 sys>Flitecom (Dave Stevens, Tom Maddy) (301) 588-1212 Для поиска в файле используется команда egrep (27.05). Команда egrep -i означает, что можно набрать torn и сценарий найдет строки, содержащие Tom, torn, ТОМ и т.д. Два имени этого сценария являются ссылками о&.оз) на один и тот же файл. Конечно, сценарий наряду с поиском номеров телефонов и адресов мог бы выполнять и другие задачи. #!/bin/sh # СОЗДАЙТЕ ССЫЛКИ phone И address НА ЭТОТ СЦЕНАРИЙ. myname=""basename $0'" # ИМЯ ДАННОГО СЦЕНАРИЯ (ОБЫЧНО address ИЛИ phone) case "$myname" in phone I address) Автоматизация офиса 791
*8.03 sysfile=/work/ora/$myname # СИСТЕМНЫЙ ФАЙЛ persfile=$(HOME?}/$myname # ПЕРСОНАЛЬНЫЙ ФАЙЛ *) echo "SO: HELP! I don't know how to run myself." 1>&2; exit 1 esac rest 44.20 if test ! -f $persfile rouch 21.07 then touch $persfile fi $# 44.15 case ?# in 0) echo "Usage: ?myname searchfor [...searchfor] (You don't tell me what you want to search for.)" 1>&2 exit 1 *) # ПОСТРОЕНИЕ ВЫРАЖЕНИЯ ДЛЯ КОМАНДЫ egrep (ВИДА аргумент1|аргумент2...) - # ИЗ ИМЕНИ (ИМЕН), ВВЕДЕННОГО ПОЛЬЗОВАТЕЛЕМ for arg do case "$expr" in "") expr="($arg" ; ; *) expr=$exprISarg" ;; esac done expr="$expr)" esac "# ПОИСК С ПОМОЩЬЮ КОМАНДЫ egrep; ИСПОЛЬЗОВАНИЕ РЕДАКТОРА sed ДЛЯ. # ДОБАВЛЕНИЯ МЕТКИ sys> В НАЧАЛО СТРОК ИЗ СИСТЕМНОГО ФАЙЛА И # МЕТКИ pers> В НАЧАЛО СТРОК ИЗ ПЕРСОНАЛЬНОГО ФАЙЛА egrep -i "$expr" $persfile $sysfile | sed -e "sS^Ssysfile:@sys>@" -e "3@ASpersfile:@pers>@" exit -В' комментариях к сценарию объясняется, что делает каждая его часть. Наверное, самую интересную часть составляют цикл for (44.16) и конструкция case (44.05), которые строят выражение для команды egrep. Если, например, ввести команду phone torn тагу, то сценарий построит и запустит командную строку egrep так, как если бы вы набрали % egrep -i ".(torn | тагу) " /u/me/phone /work/ora/phone /u/me/phone:Tom VW's mother, Barbara Van Winkel in Vermont 802-842-1212 /u/me/phone:Tom Christiansen [5/10/92] 201/555-1212 /work/ora/phone:Flitecom (Dave Stevens, Tom Maddy) (301) 588-1212 Команда se<i (34.24) превращает имена файлов, полученные от команды egrep, в метки pers> и sys>. Этот сценарий можно инсталлировать с компакт-диска или же ввести вручную. При ручном вводе поместите его в исполняемый файл phone. (Если он предназначен для всех пользователей системы, то системный администратор должен поместить этот сценарий в один из системных каталогов, например в /usr/local/bin.) Затем необходимо создать ссылку на него: % chraod 755 phone % In phone address - JP 48.03 Черновик на экране Один совет для тех, кто работает в многооконной системе. Мне часто приходится копировать и Вставлять текст с помощью мыши, и нужно иметь место, куда можно было бы временно его поместить. В частности, я объединяю вместе фрагменты из нескольких командных строк, 792 Часть цевп1вя. Разное
48.04 делаю пометки себе на память перед уходом на обед, держу под рукой отрывки текста, из которого с помощью мыши можно копировать необходимые строки. Некоторые оконные системы имеют окно буфера обмена, но я почти никогда им не пользуюсь. Вместо этого я задействую терминальные окна для создания черновиков: % cat > /dev/null Updated version number in title. Added disk icon for second edition. ICTRL^dl В данном примере я хотел выбрать один из ответов для RCS-команды а (2о.н) в другом окне. Я набрал возможные ответы, а затем скопировал один из них и вставил в другое окно, в котором выполнялась команда с/. Команда cat > dev/null воспринимает все, что я набираю, и отбрасывает цз.ы) эти строки, записывая их в "пустой" файл. Я могу набрать текст какого угодно объема, а по окончании нажать клавиши [CTRL-d] в начале строки для завершения операции ввода. - JP 48.04 Планирование работ: программа calendar Если ввести команду calendar, то появятся строки из находящегося в вашем текущем каталоге файла с именем calendar, которые будут содержать перечень работ на сегодняшний и завтрашний день. После помещения этого файла в начальный каталог системный администратор сможет запускать команду calendar и таким образом посылать с помощью утилиты mail (из) задания для каждого пользователя. Получить перечень запланированных работ можно и путем самостоятельного запуска команды calendar. К тому же вы можете послать самому себе список заданий на текущий день, можете его распечатать и т.д. Образец файла calendar, а также информация о работе одноименной утилиты приведены ниже. Чтобы выполнить поиск в файле calendar, утилита calendar строит для команды екгер (27.05) сложное выражение. При желании вы можете его просмотреть. Как работает утилита calendar Рассмотрим для начала несколько строк простого файла calendar. (Ваш файл может быть намного длиннее.) Потом я, чтобы показать, какие строки выбраны, запущу команду calendar. % cat calendar . WORK - — 12/28 Project report due tomorrow! PERSONAL * 8 rub Lisa's feet Take Lisa out to dinner on December 8 dec 9 buy Lisa lunch On 12/10, Lisa will be hungry for ice cream — BIRTHDAYS 1/1 Mom's birthday — make dinner reservations by 12/20! % date Tue Dec 8 08:43:40 PST 1992 % calendar * 8 rub Lisa's feet Take Lisa out to dinner on December 8 dec 9 buy Lisa lunch Сегодня 8 декабря. Утилита calendar находит в моем файле calendar строки, относящиеся к . сегодняшнему и завтрашнему дню. Причем она понимает много форматов даты. Дата может находиться в любом месте строки. Если какую-либо запись, например о дне рождения (здесь — Mom's birthday), оставить в файле более чем на год, утилита calendar будет Автоматизация офиса 793
48.04 напоминать о ней каждый год. Если в записи есть несколько дат, то эта запись будет отображаться по мере наступления каждой из них. (Я получу предупреждение и о дне рождения 1 января, и о том, что нужно заказать столик в ресторане до 20 декабря.) Звездочка, набранная вместо месяца, означает "все месяцы". Многие версии утилиты calendar пропускают файл calendar через препроцессор срр языка С. Кроме всего прочего, это позволяет включать в файл calendar другие подобные файлы. Строки, начинающиеся с символа # в позиции 1, читаются препроцессором. Например, следующая строка из файла calendar позволяет использовать все содержимое файла /usr/local/lib/office.calendar: tinclude "/usr/local/lib/office.calendar" Кто-нибудь (например, секретарь) может поддерживать файл office.calendar. Сотрудники, желающие получать напоминания из этого файла, могут включить в свой файл calendar строку #include. Между прочим, если строка начинается с символа # и не предназначена для препроцессора, вы можете получить загадочное сообщение об ошибке наподобие такого: calendar: 1: undefined control. Это значит, что строка 1 содержит нечто непонятное для препроцессора. Выражение для команды egrep, используемое утилитой calendar Каким образом утилита calendar находит дату в любом приемлемом формате, причем соответствующую сегодняшнему или завтрашнему дню? Она запускает системную программу, обычно имеющую имя /usr/lib/calendar, которая генерирует выражение для команды egrep -/ (27.07). Именно с помощью этого выражения находятся даты, соответствующие сегодняшнему или завтрашнему дню. Если сегодня пятница, то включаются записи, относящиеся к субботе, воскресенью и понедельнику. Вот какое выражение я получил, запустив команду /usr/lib/calendar во вторник, 8 декабря. Символы табуляции показаны буквой Т, а пробелы — символом D. $ /usr/lib/calendar " ■ П [QT(,;]) ( (([Dd]ecrOT]*|\*> [От]*| (012 112 | \*> /) 0*8) ( Г 01234 56789] |?) Г I [№(,;]) ((([Dd]ecrOT]*|\*> [Пт] * | (012 112 | \*>/> 0*9) (["0123456789] |$) Переведу первую строку этого выражения на "человеческий язык" — ведь я его привел не только для фанатиков команды egrep :-). Выражение позволит понять, какого вида даты распознает утилита calendar. Пропустим очевидные моменты, типа использования вложенных скобок, и рассмотрим суть. Если вам пока не приходилось работать с расширенными регулярными выражениями, загляните в параграф 26.04. С помощью такого выражения в файле calendar находятся строки, удовлетворяющие определенным условиям, причем само выражение следует просматривать в направлении слева направо: П [П-П,;]) Данный фрагмент соответствует тому, что идет перед датой: началу строки (Л.) либо (|) пробелу, символу табуляции, открывающей скобке, запятой или точке с запятой. Это предотвращает выбор командой egrep других слов, которые содержат сокращенное название месяца [Dd]ec. .((([М]ес[ЛОт]*|\*> [От]*| (012|12|\*)/)0*8) Этот фрагмент задает дату: выбирается Dec или dec с последующими несколькими (или ни одним) любыми символами, кроме символов табуляции и пробела ([Dd] ее [ЛС1Т] *). Этому шаблону соответствует также (|) дата, заданная таким символом, как звездочка (\*), которая означает "выбрать то же самое для любого месяца года". Дата может быть задана и (|) с помощью числового обозначения месяца (например, 012 или 12) или символа звездочки ((012Ц2|\*)) с последующей косой чертой (/). Сегодняшний день обозначается любым количеством нулей и заканчивается цифрой 8 ()0*8)): (Г0123456789] |$) Концу выражения может соответствовать любой символ в строке, кроме цифр, или же признак конца строки. Это предотвращает выбор командой egrep значений, не являющихся датами, например 8.75. 794 Часть девятая. Разное
48.05 Ну и ну! Это выражение можно повторно создать для любого другого дня, который необходимо выбрать. По пятницам вывод команды /usr/lib/calendar содержит четыре строки — по одной для пятницы, субботы, воскресенья и понедельника. Автоматизация собственного капенцаря Если календарь нужен вам каждый рабочий день, поместите в свой персональный файл crontab (40.12) строку наподобие приведенной ниже. Мы разделили ее на две, чтобы можно было напечатать, но вам необходимо ввести ее как единое целое. $USER6.aj 6 6 * * 1-5 tf=/tmp/cal.?USER; umask 077; /bin/calendar > stf; &.&.4409 test -3 ?tf SS mail -s 'Calendar for today' ?USER <$tf; rra -f $tf < 1.1.01 Эта строка обеспечивает ежедневный (имеются в виду рабочие дни) запуск команды calendar из моего начального каталога в 6 часов 06 минут утра (40.os). В ней определяется переменная tf интерпретатора Bourne shell (ь.щ, содержащая имя временного файла в каталоге /tmp (21.113), а также устанавливается значение umask (22.04), предотвращающее доступ к файлу других пользователей. Затем запускается команда calendar, и ее вывод сохраняется во временном файле. Если в календаре имеются данные, относящиеся к сегодняшнему дню, то команда mail пересылает их мне. (Опция s команды mail обеспечивает добавление в поле subject: строки Calendar for today. Она, в общем-то, не нужна.) И наконец, временный файл удаляется. Не имея персональных файлов crontab, вы можете вместо них использовать самозапускающиеся of-задания (40.ой), а также сценарий nextweekday (40.10). - JP 48.05 Команда leave: настойчивые напоминания о том, что пора уходить Вы спокойно работаете, и вдруг на экране появляется сообщение Time to leave! (Пора уходить!) и звучит сигнал терминала. Минуту спустя появляется сообщение You're going to be late! (Ты можешь опоздать!). Не может быть! Неужели мама научилась пользоваться и командой write (1.33р. Нет, это программа leave напоминает вам о предстоящей встрече. Немного раньше она уже сообщила, что вы должны уйти через пять минут. Если в вашей системе есть команда leave, можете запустить ее одним из следующих трех способов. • Команда leave 1300 устанавливает время выдачи предупреждения равным 13:00. • Команда leave +30 устанавливает время выдачи предупреждения через 30 минут после ее задания. • Команда leave без аргументов выдает запрос When do you have to leave? (Когда вам нужно уходить?). Можно ввести ответы наподобие приведенных выше: 1300 или +30. Если же просто нажать [RETURN], то команда leave оставит вас в покое. Эту команду удобно поместить в свой файл .login или .profile (2.02). Когда эта команда прекратит вас изводить? При выходе из системы выполнение команды leave автоматически прекращается. Новые версии команды, кроме того, завершаются через 10 минут, выводя сообщение That's was the last time I'll tell you. Bye. (Это было последнее предупреждение. До свидания.). Более старые версии продолжают работать бесконечно. В некоторых версиях команды leave нельзя задать выдачу предупреждения на любое время завтрашнего дня. Однако для запуска команды leave после полуночи можно воспользоваться командой sleep (40.02). Предположим, сейчас 10 часов вечера, а вы хотите уйти в час ночи. Полночь наступит через 2 часа, или 7200 секунд (60x60x2). Добавьте для надежности еще 10 минут (600 секунд) и введите ()13.(I7 $ (sleep 7800; leave 100) & 1234 Можно также уничтожить процесс leave, но для этого необходимо использовать надежное средство — сигнал с номером 9 (3S.os). Чтобы увидеть, как процесс leave выполняется в фоновом Автоматизация офиса 795
4806 режиме, и получить его идентификатор процесса (зв.оз), следует применить команду £S сшу с опцией -х. Вывод команды ps можно отфильтровать с помощью команды grep leave: % ps x I grep leave 6914 p3 S 0:00 leave 19823 p3 R 0:01 grep leave % kill -9 6914 - JP 48.06 Получение календаря на любой месяц любого года: программа cal Вы знаете, в какой день недели родились? Если вы появились на свет 23 сентября 1962 года, введите следующую команду, и вы получите календарь на тот месяц: % cal 9 1962 September 1962 Su Mo Tu We Th Fr Ea 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 '23 24' 25 26" 27 28 29 30 Вы родились в воскресенье. При указании года обязательно набирайте все четыре цифры. Если этого не сделать, то получится календарь на 62-й год, относящийся к первому веку нашей эры! Чтобы вывести календарь на текущий месяц, просто введите cal (без аргументов). Можно также получить календарь на любой год, задав один аргумент — год (для указания любого ГРДа в последнем тысячелетии используйте четыре цифры). . Создается впечатление, что в календаре на сентябрь 1752 года есть ошибка: % cal 9 17S2 September 1752 Su Mo Tu We Th Fr Sa 1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 . . Но на самом деле, это не ошибка. Именно тогда Великобритания, а позже и ее американские колонии приняли Григорианский календарь. День, который должен был быть 3 сентября, . стал L4 сентября, а начало года передвинулось с 25 марта на 1 января. Побойтесь бога! . Ошибки в UNIX? :-). -JP 48.07 Команда cal, выделяющая сегодняшнее число Если вы, Подобно мне, забываете, какое сегодня число :-), то календарь, выводимый описанной выше командой са[ (4S.06), мало чем поможет. Предлагаем вашему вниманию небольшой сценарий, вставляющий вокруг текущего числа угловые скобки. Например, еели сегодня 7 августа 1996 года, вы увидите следующее: % cal August 1996 Su МО Tu We Th Fr Sa 12 3 4 5 6 >7< 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 796 Часть девятая. РрзМ
48.08 .Если вы уверены,- что этот сценарий никогда не будет вызван другой программой, . рассчитывающей на его системную версию, можете назвать его также cal. He забудьте только поместить его в каталог, указанный в вашей переменной PATH перед именем каталога /usr/bin (8.07), т.е. каталога, содержащего системную версию программы cal. В противном случае дайте сценарию другое имя, например caljoday. Сценарий присваивает параметрам командной строки значения, выведенные командой date, добавляя для надежности впереди х (в том случае, если команда date ничего не выводит, команда set все равно будет иметь аргументы и не станет выводить список всех своих переменных интерпретатора shell). Теперь эти параметры выглядят следующим образом; х Wed Aug 7 20:04:04 PDT 1996 И четвёртый Параметр (номер дня) как раз используется в сценарии: #! /bin/sh If Если пользователь не вводит аргументов, # поместить скобки > < вокруг сегодняшнего числа: case $# in 0) eval "date +d=%d\ #Переменной d присвоить номер текущего дня /usr/bin/cal | # Поместить скобки > < вокруг текущего дня sed -e 's/V /' -e "s/$d />$d</" -e "s/ $d/>$d</" -e "з/ $d />$d</" *) /usr/bin/cal "$@" ;; esac Если вы вводите какие-нибудь аргументы, то сценарий полагает, что вам нужен не текущий месяц, и запускает системную команду cal. В противном случае он передает по каналу вывод этой команды редактору sed (34.24). С его помощью перед каждой строкой вставляется пробел, освобождая место для скобки (>). Далее выполняются три команды подстановки — для случаев, когда номер текущего дня находится в начале, конце и середине строки. - JP 48.08 Календарь для терминалов и принтеров с длиной строки в 132 символа Программа са[ (48.06) удобна, но довольно скромна. Если у вас есть терминал с длиной строки в 132 символа или (что более важно) принтер с такой длиной строки, попробуйте запустить вместо cat программу calen. И Программа calen выводит календари более широкого формата (132 позиции) и вычерчивает для каждого числа прямоугольные области, куда можно вписывать заметки. Синтаксис команды calen: calen месяц год [длина] Длина является необязательным параметром, задающим количество месяцев, которые нужно ; - отобразить, начиная с указанного (количество месяцев по умолчанию равно I). Например, команда • %-calan 6 1965 показывает календарь на июнь 1965 года. Чтобы получить календарь на весь 1965 год (все 12 месяцев), выполните команду % calen 1965 12 Единственным доводом в пользу применения команды calen является возможность распечатать календарь, с тем чтобы впоследствии делать на нем заметки. Но если у вас есть принтер, понимающий язык PostScript, забудьте о программе calen, поскольку программа peal (48.09) намного мощнее. - LM Автоматизация офиса 797
48.09 48.09 Кзлеидзри в формате PostScript, созданные с помощью программы peal Иногда нужно получить копию календаря на бумаге. Ее можно создать с помощью команды cal (4s.o6) и преобразователя ASCII-PostScript, например pstext (43.22), однако такой календарь будет иметь не очень привлекательный внешний вид. И Рекомендуем воспользоваться программой peal, с помощью которой можно не только выводить действительно красивые календари, но и выполнять более широкий объем работ. Совсем недавно я узнал о существовании программы peal, но она уже имеет шанс стать моей peal любимой утилитой. По умолчанию программа peal просто создает календарь в формате PostScript на текущий месяц. PostScript-команды записываются в стандартный вывод, поэтому их необходимо передавать по каналу команде Ipr в системах на базе BSD или команде 1р в системах типа System V. Задав команду % peal | Ipr вы получите календарь на текущий месяц, занимающий целую страницу. Числа, соответствующие субботе и воскресенью, будут выделены серым цветом. Чтобы получить календарь на другой месяц, задайте его таким же способом, как и в случае команды cal. Так, календарь на ноябрь 1999 года можно получить посредством команды % peal 11 1999 | Ipr Для создания календаря на целый год предназначена опция -w. t peal -w I Ipr Увидев вывод команды peal, вы поймете, насколько он лучше по сравнению с выводом команды cal. К тому же программа peal предоставляет возможность сконфигурировать календарь. Программа peal ищет файл .calendar либо в вашем начальном, либо в текущем каталоге. (Это скрытый файл (I6.ii), поскольку его имя начинается с точки.) Данный файл используется и для выделения отдельных дней года. В частности, его можно применить для обозначения определенных дней как выходных: .1/1 New Year's Day (Новый год) Feb 14 Valentine's Day (День ев. Валентина) Однако программа peal понимает и ограниченный набор слов, косвенно указывающих на дату. Например: Second Sunday in May Mother's Day (День матери) 4th thu of nov Thanksgiving (День благодарения) В каждом из этих примеров дата задана определенным текстом. Если после даты поставить звездочку (*), то этот день будет помечен как выходной. 1/1* New Year's Day Feb 14 Valentine's Day Second Sunday in May Mother's Day 4th thu of nov* Thanksgiving Это означает, что данное число в календаре будет выделено серым цветом — аналогично числам, соответствующим субботе и воскресенью. Но более важно другое: программа pea! будет понимать, что этот день не является рабочим. С помощью программы peal наряду с выходными можно помечать такие даты, как дни рождения и годовщины. June 4 My Birthday!!! (4 июня » Мой день рождения!!!) 798 Часть девятая. Разное
48.09 September 3 Peter's birthday (3 сентября День рождения Питера) April 1 Mom and Dad's anniversary (1 апреля Годовщина свадьбы родителей) А теперь несколько слов о том, благодаря чему программа peal может стать по-настоящему нужной и полезной: ее можно использовать для напоминания о запланированных встречах: April 14 Meet with tax attorney at 4:45 (14 апреля Встреча с налоговым инспектором в 4:45) Last day of October Dr. Jekyl's office, 5:30 (Последний день октября Офис д-ра Джекила, 5:30) или о регулярных встречах: Each Friday of July Leave early for Cape (Каждую пятницу в июле Уходить пораньше для поездки на мыс) Как вы уже могли заметить, синтаксис команды peal очень гибок и вместе с тем он достаточно сложен. Но мы не станем его здесь объяснять, так как программа peal поставляется с полной документацией, а команда peal -я выводит список синтаксических правил и опций командной строки. Запустив эту команду, вы сможете быстро освоить все правила. Я не хотел бы на этом закончить свой рассказ. Продолжим знакомство с программой и рассмотрим еще несколько интересных ее возможностей. Так, вместо названия месяца можно использовать ключевое слово all, означающее "все месяцы", т.е. весь год. Например: First day of all Send monthly report to boss (Первый день каждого месяца Представлять отчет руководству) Each Monday of all Status meeting at 11:30 (Каждый понедельник Планерка в 11:30) Здесь мы, воспользовавшись фразой First day of all, задаем день представления месячного отчета. А если первый день месяца выпадает на субботу, воскресенье или праздник? Ничего страшного. Программа peal понимает и такие слова, как workday (рабочий день) или holiday (праздник). Чтобы обеспечить вывод напоминания в первый рабочий день месяца, необходимо задать First workday of all Send monthly report to boss По умолчанию программа peal предполагает, что нерабочими днями являются только суббота и воскресенье. К этим дням добавляются праздничные, определяемые с помощью звездочек, как описано выше.' Еще одна возможность программы peal — это задание фаз луны в файле спецификаций дат. Second full_moon in all Blue moon! (Каждое полнолуние Голубая луна!) Для более подробного описания даты программа peal предоставляет спецификаторы форматов. Предположим, например, что в начале каждого месяца мы хотим получать напоминания о необходимости составить месячный план. С этой целью нужно ввести такую строку: First workday of all Write schedule for %B (Первый рабочий день каждого месяца Написать план для %В) Спецификатор формата %В заменяется названием текущего месяца. Поэтому эта же фраза для первого рабочего дня октября будет выглядеть как Write schedule for October. Примерами спецификаторов, определенных в программе peal, могут служить: %А — для названия дня недели, %d — для числа, %У — для года, % j — для номера дня в году, %1 — Обратите внимание на то, что программа peal знает только о тех праздниках, которые объявлены в вашем файле спецификаций. Поэтому, как правило, необходимо заранее определить праздничные дни в файле .calendar, чтобы последующие ссылки на рабочие дни соответствовали действительности. Автоматизация офиса 799
48.10 для количества дней, оставшихся до конца года. Имеются и другие спецификаторы. Самыми полезными из них являются %+ и %-, задающие следующий или предыдущий месяц. Предположим, что нам нужно получить напоминание в последний день месяца о необходимости внести арендную плату за следующий месяц. Тогда мы должны ввести Last day of all Pay rent for month of %+B (Последний день каждого месяца Внести арендную плату за месяц %+В) Если вы должны сдавать отчет два раза в месяц, скажем 1-го и 16-го числа, следует задать first workday of all Time sheet due for end of %-B (Первый рабочий день каждого месяца Отчет за вторую половину %-В) workday on_or_after all 16 Time sheet due for beginning of %B (Рабочий день 16-го числа или после него Отчет за первую половину %В) Можно также задать опции командной строки для команды peal в своем файле .calendar, используя ключевое слово opt: opt -n Times-Italic -m Опция -n Times-Italic заставляет программу peal использовать для отображения даты шрифт Times Italic. Опция -т задает отображение дат лунных фаз. - LM 48.10 Работа с фамилиями и адресами Наиболее часто утилита awk (зз.п) применяется для построения базы данных фамилий и адресов. Вам при этом необходимо упорядочить все сведения в виде запиеей и написать программу, извлекающую информацию из записей и отображающую ее в отчетах. В сценариях, приведенных в данном параграфе, вместо утилиты awk используется утилита nawk (н.в), однако принцип остается тем же. Прежде всего необходимо определить структуру записи. Нам, в частности, нужно иметь следующие поля: Name (Фамилия) Street (Улица) City (Город) State (Штат) Zip (Почтовый индекс) Однако возможна и более сложная структура записи, например такая: Name (Фамилия) Title (Должность) Company (Компания) Division (Отдел) Street (Улица) City (Город) State (Штат) Zip (Почтовый индекс) Phone (Телефон) Fax (Факс) Email (Адрес электронной почты) Directory (Начальный каталог) Comments (Примечания) При программировании не имеет значения, сколько полей содержит запись. Главное, чтобы структура записи была определена до начала процесса программирования. 800 Часть девятая. Равное
48.10 Далее мы должны решить, как отличать одно поле от другого и одну запись от другой. Если записи короткие, вы можете держать по одной записи в строке и использовать символ - в качестве разделителя полей: Name~Street~City~State--Zip Namel~Streetl~Cityl~Statel~Zipl Однако такие записи неудобно редактировать. (Мы не станем писать программы для автоматизации процесса ввода данных, а будем полагать, что записи уже созданы с помощью редактора, например vi или Emacs.) Мы можем разместить каждое поле в отдельной строке и разделить записи с помощью пустых строк: Name Street City State Zip Namel Streetl Cityl State1 Zipl Это хорошее решение. Необходимо лишь следить за тем, чтобы сами данные не содержали пустых строк. Например, если нужно добавить поле, в которое будет заноситься название компании, но не во всех записях это поле будет заполнено, то для обозначения пустого поля необходимо использовать какой-нибудь символ-заполнитель. Еще одно решение состоит в помещении каждой записи в отдельный файл, а полей — в отельные строки. Именно такая организация записей будет применена в нашей программе. Она обладает двумя преимуществами: позволяет иметь записи разной длины и не требует специальных разделителей. Поэтому такую запись легко создать и отредактировать. Несложно также отобрать несколько записей для обработки. Каждому файлу мы дадим имя, однозначно идентифицирующее его в текущем каталоге. Вот простой пример записи, содержащейся в файле pmui: Peter Mui International Sales Manager O'Reilly & Associates, Inc. East Coast Division 90 Sherman Street Cambridge MA 01240 617-354-5800 617-661-1116 peterSora.com /home/peter Any number of lines may appear as (Примечание может содержать a comment. любое количество строк) Эта запись состоит из 13 полей, каждое из которых может быть пустым (однако для сохранения позиции в этом месте должна быть пустая строка), а последнее поле может содержать сколько угодно строк. Наша запись не содержит меток, указывающих, что находится в каждом поле. Хотя эту информацию и можно было поместить в самой записи, лучше хранить метки отдельно — в таком случае их проще изменять. (Можно создать шаблон записей, включающий метки, с помощью которых поля различаются при добавлении новой записи.) ШоШтизацня офиса Щ. 9-171 801
48.10 Мы поместили метки полей в отдельный файл diet, который здесь не рассматривается, так как в нем описана структура записей, приведенная выше. Создадим три программы, которые вызываются одинаковым образом: команда список_записей Список_эаписей содержит одно или несколько имен файлов. Для задания нескольких записей в командной строке можно, конечно, использовать метасимволы. Первая программа (read.base) читает файл diet, чтобы получить метки полей и вывести запись в форматированном виде: % read.base pmui 1. 2. 3. 4 . 5. 6. 7. 8. 9. 10. 11. 12. 13. Name: Title: Company: Division: Street: City: State: Zip: Phone: Fax: Email: Directory: Comments: a comment. Peter Mui International Sales Manager O'Reilly & Associates, Inc. East Coast Division 90 Sherman Street Cambridge MA 01240 617-354-5800 617-661-1116 peter@ora .com /home/peter Any number of lines may appear аз Программа read.base сначала выводит название поля, а затем его содержимое. Просмотрим текст этой программы. nawk 'BEGIN { FS=":" # Проверяется, задана ли хотя бы Одна запись if (ARGC < 2) ( print "Please supply record list on command line" exit } # Имя локального файла, содержащего метки полей: record_template = "diet" # Цикл чтения файла меток полей # field_inc = количество полей # fields[] = индексированный массив меток field_inc=0 while ((getline < record template) > 0) ( ++field_inc \ fields[field_inc] = $1 \ ) field__tot=field_inc ) # Теперь читаются записи. # Вывод имени файла для каждой новой записи: FNR == 1 ( field_inc=0 print "\n" FILENAME ":" ) { 802 Часть девятая. Разим
48.10 # Вывод номера поля, его метки и содержимого. # Последнее поле может содержать любое количество строк без меток. if (++field_inc <= field_tot){ if field_inc >= 10) space = ". " else space = ". " print field_inc space fields[field_inc] ":\t" $NF ) else print $NF }' $* Обратите внимание, что программа не осуществляет проверки ввода. Если запись не содержит названия отдела (поле Division) и вы не оставили четвертую строку пустой, то содержимое этой строки попадет в поле Division:, несмотря на то, что на самом деле это название улицы. Программа read.base проверяет лишь синтаксическую корректность данных в файле. Задав более одной записи, мы получим вывод содержимого всех этих записей в том порядке, в каком они указаны в командной строке. Вторая программа называется mail.base. Она формирует наклейку с адресом: % mail.base pmui Peter Mui International Sales Manager O'Reilly & Associates, Inc. East Coast Division 90 Sherman Street Cambridge, MA 01240 Задав список_записей, мы получим несколько наклеек. Вот текст программы mail.base: nawk 'BEGIN { FS="\n" # Проверяется, указал ли пользователь запись if (ARGC < 2) { print "Please supply record list on command line" exit ) ) # Пропуск пустых строк /Л$/ { next ) # Следующий код жестко привязан к формату записи; # вывести первые 5 полей, а затем в одной строке вывести # название города, запятую, название штата и почтовый индекс < if (FNR < 6) print $0 else if (FNR == 6) printf $0 ", " else if (FNR = 7) printf $0 else if (FNR = 8) printf " " $0 "\n\n" }' $* ЛЬйШтизация офиса 26* 803
шо В зависимости от того, какую информацию нужно получить, вы можете написать разные варианты этой программы. Последняя программа называется list.base. Она формирует табличный список фамилий и соответствующих им записей и позволяет выбирать конкретную, запись: % list.base lwalsh pmui jberlin # NAME S COMPANY FILE 1. Linc3a Walsh, O'Reilly S Associates, Inc. lwalsh 2. Peter Mui, O'Reilly & Associates, Inc. pmui 3. Jill Berlin, O'Reilly & Associates, Inc. jberlin Select a record by number: 2 После выбора пользователем номера записи последняя выводится с помощью команды read.base. Я не обеспечил возможности постраничного просмотра, поэтому список будет непрерывно прокручиваться, не останавливаясь примерно через каждые 24 строки (а это было бы нелишним). Вот текст программы list.base: nawk 'BEGIN { # Проверка введенного пользователем перечня записей if (ARGC < 2) { print "Please supply record list on command line" exit } # Определение формата строки отчета FMTSTR = "%3s %-40s %-15s\n" # Вывод заголовка отчета printf(FMTSTR, "#", "NAME S COMPANY", "FILE") # В каждой записи найти фамилию, должность, название компании и вывести их inc=0 for (х=1; х < ARGC; x++){ getline NAME < ARGV[x] getline TITLE < ARGV[x] getline COMPANY < ARGV[x] record_list[x] = ARGV[x] printf(FMTSTR, ++inc ".", NAME ", " COMPANY, ARGV[x]) } # Предложить пользователю выбрать запись по номеру printf "Select a record by number:" getline answer < "-" # Для .отображения выбранной записи вызвать программу read, base system("read.base " record_list[answer]) } > $* Для изучения различных блоков информации, содержащихся в наборе записей, можно написать другие версии этой программы. В параграфе 45.22 показан сценарий, создающий законченную систему сбора имен и адресов. (Сценарий нужно изменить, с тем чтобы он вместо пустого поля выводил пустую строку и не записывал метки в файл.) - DD 804 Часть девятая. Разное
шп 48.11 Программа index и работа с базами данных Достаточно гибкая программа index предназначена для работы с базами данных. Программа хорошо построена: для каждой базы, данных она поддерживает два файла — файл описания полей и файл отсортированных данных. Понять ее работу можно интуитивно, просто запустив программу. Чтобы иметь возможность использовать эту программу, необходимо сначала создать каталог базы данных. По умолчанию программа index ищет каталог $HOME/.index. 14.11 % rakdir -/.index Далее, при первом запуске программы index, будет выведен запрос имени базы данных, с (§) 1 которой вы хотите работать: mhx Select a database: Если вы зададите имя пока не существующей базы данных, то программа будет считать, что нужно создать новую базу. Будет запущен редактор, указанный в переменной среды EDITOR (6.03), и вы сможете ввести названия всех полей новой базы данных. Поддерживается до 16 полей. Например, можно создать базу данных с именем addresses и определить для нее следующие поля: NAME TITLE ORGANIZATION - STREET ADDRESS CITY STATE ZIP CODE VOICE PHONE NUMBER FAX PHONE NUMBER EMAIL ADDRESS После выхода из редактора вы попадаете в главное меню: Database: addresses (0 entries) а - Add new entry to database (Добавить в базу новую запись) f - Find entry in database (Найти запись) г - Read database entry by entry (Читать по одной записи) s - Save modifications, do not exit (Сохранить изменения; не выходить) q - Save modifications, exit (Сохранить изменения и выйти) х - Exit (Выйти) Command: В новой базе данных нет записей, но вы уже можете начинать их вводить, нажав предварительно клавишу а. В режиме добавления записей в базе данных выводятся запросы для каждого поля, определенного при создании. Заполнив поле, нажмите клавишу [RETURN], чтобы перейти в следующее поле. Допускается также использование команд редактирования, например [CTRL-d] — для удаления вплоть до конца строки или [CTRL-p] — для перемещения на строку вверх. Закончив ввод всей записи, нажмите клавишу [ESQ. NAME: TITLE: ORGANIZATION: STREET ADDRESS: CITY: STATE: Stella Rosenzweig Mom 2456 Bronx Park East Bronx EJY Автоматизация офиса 805
48.11 ZIP CODE: VOICE PHONE NUMBER: FAX PHONE NUMBER: EMAIL ADDRESS: 10467 718-231-2619 После сохранения новой записи вы возвратитесь в главное меню. Теперь можно добавить другие записи, просмотреть существующие, произвести поиск строки в одной из существующих записей или выйти из программы. Чтобы найти существующую запись, находясь в главном меню, нажмите f. Вы получите предложение ввести шаблон поиска: Pattern to search for: Ben Программа index ищет все записи, соответствующие шаблону. В шаблоне поиска допускается также использование регулярных выражений (2вм). Все записи, содержащие искомую строку, по очереди представляются на экране: NAME: TITLE: ORGANIZATION: STREET ADDRESS: CITY: STATE: ZIP CODE: VOICE PHONE NUMBER: FAX PHONE NUMBER: EMAIL ADDRESS: Benjamin Braddock Sales Manager Acme Plastics, Inc 103 Morris St., Suite A Tucson AZ 85472 800-998-9938 800-999-9999 ben@acme.com <RET> = next entry (следующая запись) "-" = previous entry (предыдущая запись) "q" = return to main menu (возврат в главное меню) "d" = delete this entry (удалить эту запись) "e" = edit this entry (редактировать эту запись) Command: Поиск записей можно также вести, указав шаблон в командной строке программы index. Чтобы это сделать, введите как имя базы данных, так и шаблон. (Заключите шаблон в одинарные кавычки (8.N), если хотите использовать регулярное выражение.) % index addresses '[ЬВ]еп' Все "подходящие" записи будут направлены на стандартный вывод. Самым типичным случаем применения такого рода баз данных является введение и хранение информации о людях. Однако программу index можно использовать и для хранения любой другой информации, скажем о ближайших ресторанах: NAME: STREET ADDRESS: CITY: PHONE: RESERVATIONS (y/n): FOOD (l=poor, 10-excelent): DECOR (l=poor, 10-excelent): SERVICE (l=poor, 10-excelent) RECOMMENDED DISHES: STAY AWAY FROM: RECOMMENDED BY: Rooster BBQ & Grill 1122 Rooster Ave. Cambridge 555-1212 n 8 2 6 Baby-back ribs, Elvis pizza Pulled turkey plate, mango pie Jerry, Boston Phoenix Для достижения еще большей гибкости программы index ее вывод можно отфильтровать (ie.w- - LM 806 Часть девятая. Разное
48.12 48.12 Использование программы index с фильтром Особенно удобная возможность программы index (4s.n> обеспечивается ее опцией -/ выполняющей фильтрацию вывода. При указании этой опции программа index выводит столбцы, разделенные символами табуляции. Вместо отдельного вывода каждой подходящей записи вверху выводятся заголовки полей, а каждая запись помещается под ними; значения полей располагаются в колонках, разделенных символами табуляции: % index -£ 'sed s/Mui/Mud/' addresses Peter NAME TITLE ORGANIZATION STREET ADDRESS CITY STATE Henry K. Smith Peter Johnson & Associates 324 Bur Peter Mud International Sales Manager O'Reilly and As ... Peter L. Loos President Introspective Solutions, Inc. ... Поля, разделенные символами табуляции, конечно же, не будут выстроены на экране идеально ровно, однако станет проще обрабатывать столбцы с помощью программ cut (З5.щ, awk (Зз.п> или perl (37.01). Например, команду cut можно использовать в качестве фильтра Для ограничения вывода несколькими важными полями: % index -£ 'cut -£1,8,10' addresses '.' NAME VOICE PHONE NUMBER EMAIL ADDRESS Henry K. Smith 617-555-1212 henry@pja Paul S. Spencer 617-693-1111 paul@lotus.com Peter Mui 800-698-9938 peter@ora.com (Мы использовали символы ' .' в качестве шаблона, которому соответствуют все записи.) Вы можете использовать сценарий в качестве фильтра и поместить его в свой каталог $HOME/.index с суффиксом .fint. Такой прием оказывается очень удобным, когда дело касается сложных программ-фильтров. Если вы, предположим, хотите иметь возможность правильно читать вывод предыдущего примера, попробуйте применить утилиту awk, как показано в параграфе 35.22, или же программы tbl (43.is) и nroff (43.13). Чтобы сделать это за один прием, воспользуйтесь фильтром, приведенным ниже [Линда — прекрасная машинистка. Она использует команду cat » (25.02) для написания коротких сценариев (и одновременного отображения сценария на экране). Такие текстовые редакторы, как Emacs и vi, также подойдут. — JP\: ~ 14.11 % cat » -/.index/printinxo.fmt #! /bin/sh cut -£1,8,10 | sed ' li\ .TS\ 111. $a\ ■ТЕ' I tbl | nxoff | col ICTKL-dl j ehmod +x 1.05 % chmod +x -/. index/printinfо. fmt % index -£ printinfo addresses '.' NAME VOICE PHONE NUMBER EMAIL ADDRESS Henry K. Smith 617-555-1212 henry@pja Paul S. Spencer 617-693-1111 paul@lotus.com Peter Mui 800-698-9938 peter@ora.com - LM •Ллтошиэация офиса 807
49 Работа с числами 49.01 Программа be: простые математические операции в командной строке Хотите выполнить несколько элементарных вычислений? Стандартная UNIX-система предлагает два простых калькулятора: dc {desk calculator — настольный калькулятор) и be. (Кто знает, что означает буква Ь? В документации эта программа носит название "арифметический калькулятор с произвольной точностью".) Для новичков лучше подойдет калькулятор dc. Однако это калькулятор с обратной польской записью. Сначала вводятся операнды, каждый — в отдельной строке, а затем — оператор. Операнды сохраняются в стеке. Оператор извлекает их оттуда, меняя на результат. Результат не выводится на экран. Чтобы увидеть его, нужно набрать команду р. Калькулятор be намного удобнее: % Ьс 5*2 10 ICTRL-dl -. % Введите арифметическое выражение и нажмите [RETURN]. Для выхода из программы нажмите клавиши [CTRL-d]. Единственное, что необходимо изучить, чтобы калькулятор be стал по-настоящему полезным, — это команда scale, которая сообщает калькулятору, сколько десятичных разрядов выводить. По умолчанию это значение равно 0, поэтому ввод такого выражения, как 10/4, приводит к неточному результату — 2. А вот команды %. Ьс 10/4 2.50 обеспечивают более точные результаты, поскольку задают два десятичных разряда. Разрядность можно изменять в пределах от 0 до 99 десятичных знаков. В качестве альтернативы можно вызвать команду be с опцией -/, которая автоматически выдает до 20 десятичных разрядов (и множество завершающих нулей при простом делении.) Программа be обладает широким кругом возможностей — она даже позволяет определять собственные функции. За более подробной информацией обратитесь к соответствующему разделу документации. Программа be удобна и для преобразования чисел из одной системы СЧИСЛеНИЯ В ДРУГУЮ (49.02). Команду ехрг можно использовать для проведения простых математических операций (49.06) в командной строке, однако она больше подходит для вычислений в сценариях shell (4S2S)- Система X Window (ui) предоставляет команду xcalc, выводящую на экран калькулятор фирмы Texas Instrument или HP, работа с которым осуществляется с помощью клавиатуры или мыши- - TOR .цф. Ш Часть девятая. Ррн*
49.03 49.02 Программа be: преобразование чисел в шестнадцатеричный и двоичный формат Программа be — удобное средство преобразования чисел из одной системы счисления в другую. По умолчанию программа Ьс принимает и выводит десятичные числа. Однако с помощью параметров ibase и obase можно задать ввод/вывод в другой системе счисления, например в шестнадцатеричной или двоичной. Скажем, чтобы найти десятичный эквивалент шестнадцатеричного числа, установите для параметра ibase значение 16, а значение obase не меняйте (оставьте равным 10). Затем введите число (или последовательность чисел, разделенных точками с запятой), подлежащее преобразованию, и нажмите [ENTER]. Десятичный эквивалент будет выведен ниже. (Шестнадцатеричные цифры от А до F должны быть прописными, иначе программа be будет сообщать об ошибке.) Например: % Ьс ibase=16 B6;7F;FFF 182 127 4095 Для преобразования десятичного числа в шестнадцатерияное необходимо установить для параметра obase значение 16, а для ibase оставить 10: % Ьс оЬам*16 142 8Е Чтобы преобразовать двоичное число в шестнадцатеричное, следует выполнить такие установки: ibase=2 и obase=16 (или ibase=16 и obase=2 для обратных преобразований): % Ьс obase=16 ibase«=2 11010001 Dl Для выхода из программы be нажмите [CTRL-d]. He забывайте, что сначала устанавливается значение параметра obase, а затем — ibase. Если не придерживаться такого порядка, возникнут Проблемы (49.03). - TOR 49.03 Проблемы при преобразовании чисел из одной системы счисления в другую Предположим, необходимо преобразовать восьмеричное число в шестнадцатеричное, и вы набрали: % Ьс ibase=8 оЬаае=1б 17 11 Неправильный ответ! Восьмеричное 17 должно превратиться в шестнадцатеричное F, а не в 11. В чем же ошибка? Работа с числами. 80S
49.04 Из-за того что значение параметра ibase (8) установлено до задания параметра obase (16), программа посчитает, что последнее введенное значение равно восьмеричному 16 (десятичному 14)! Итак, всегда задавайте параметр obase до установки значения ibase. Рассмотрим аналогичную ситуацию. Допустим, для параметра ibase установлено значение 16, а вы хотите вернуться к его значению 10. С этой целью необходимо набрать: ibase=A а не ibase=10 Не пытайтесь вводить шестнадцатеричные числа, если значение ibase не равно 16. Программа be недостаточно "умна", чтобы отвергнуть ваш ввод, и в ответ выведет непонятные сообщения. - TOR 49.04 Аргументы функций синуса и косинуса должны задаваться в радианах Однажды во время работы с программой be мне довелось использовать библиотеку математических функций, которая в этой утилите вызывается с помощью опции -/. Мне нужно было произвести простые вычисления с синусами. Ознакомившись с документацией, я решил, что мне необходимо лишь предоставить функции sine значение угла в градусах. Например, я думал, что синус 30 градусов можно получить посредством следующей команды: 8(30) Однако выполнение данной команды не принесло ожидаемых результатов. Небольшое исследование показало, что угол должен быть выражен не в градусах, а в радианах. Например, синус 30 градусов, или 0,5263 радиана, s(.5263) дает правильный результат — 0,5. То же касается и функции cosine. — MS, из телеконференции comp.unix.questions в Usenet, 19 апреля 1989 г. 49.05 Преобразование чисел из одной системы счисления в другую с помощью программы cvtbase И Хотя программа be позволяет выполнять преобразование чисел из одной системы счисления в другую (49.02), в этих целях удобнее применять программу cvtbase, которая именно для этого и предназначена. Например, для преобразования десятичного IP-адреса в его шестнадцате- evtoese ричный эквивалент следует ввести такую команду: % cvtbase d h 140.186.65.25 8с.ba.41.19 Первый аргумент (d) означает, что ввод производится в десятичном формате. Второй аргумент (h) указывает, что мы хотим получить вывод, преобразованный в шестнадцатеричный формат. Общий синтаксис вызова программы cvtbase следующий: cvtbase основание_входных_даиных основание_выходных_данных Параметр основание может принимать следующие значения: d, D Десятичные числа, состоящие из цифр от 0 до 9. х, h Шестнадцатеричные числа (со строчными буквами), состоящие из цифр от 0 до 9 и из букв от а до f. 810 Часть девятая. Резное
49.07 X, н Шестнадцатеричные числа (с прописными буквами), состоящие из цифр от 0 до 9 и из букв от А до F. о, о Восьмеричные числа, состоящие из цифр от 0 до 7. Ь, в Двоичные числа, состоящие из цифр 0 и 1. Другие символы, которые не входят в указанный набор, выводятся без изменений. В предыдущем примере точки в IP-адресе сохранены и в шестнадцатеричном выводе. - LM 49.06 Команда ехрг: простые арифметические операции Назначение команды ехрг — выполнение арифметических операций. Она предназначена не только для инкрементирования переменных в циклах интерпретатора Bourne shell, но и для выполнения простых целочисленных вычислений в командной строке. Например, команда wc сообщила, что файл содержит 2545 строк. Мне нужно узнать, сколько это займет страниц, если разместить на каждой из них 66 строк. Для этого я ввожу такую команду: $ ехрг 2545 / 66 38 Итак, я получу 38 страниц, точнее — 39, поскольку команда ехрг округляет результат в меньшую сторону. В параграфе 45.28 приведен полный перечень математических операторов команды ехрг. - JP 49.07 Сценарий addup: определение итогового значения в столбце ^^^Ш Некоторые программы выводят информацию в столбцах. Сценарий addup суммирует числа, I (0) 1 содержащиеся в указанном столбце. Он читает данные из файла или стандартного ввода. Рь_^| Например, команда lastcomm показывает в 4-м столбце, сколько времени центрального tddup процессора использовано: % lastconm tcomm sleep tcomm — 0.08 sees Thu Mar 27 10:23 date tcomm — 0.08 sees Thu Mar 27 10:23 tail tconrni — 0.09 sees Thu Mar 27 10:23 pwho tconrni — 0.30 sees Thu Mar 27 10:23 % lastcomm tcomm I addup 4 0.550000 Команда grep -c (is.os) выводит количество совпадений с шаблоном в каждой строке после двоеточия. Чтобы сложить эти значения, нужно пропустить вывод команды grep через фильтр sed, который удалит имена файлов и двоеточие, и суммировать значения первого столбца с помощью сценария addup: % grep -с CAUTION *.txt abar.txt:0 applic.txt:3 badprob.txt:2 35 % gzep -c CAUTION *.txt I sed 's/.*://' I addup 1 317 А вот и сам сценарий addup: сазе "$1" in [1-9]*) colnum="$l"; shift;; *) echo "Usage: 'basename $0" colnum [files]" 1>&2; exit 1;; esac Работа с числами 811
4Ш # Вывод в формате целых чисел, но переключение на формат %.4, # если в© входных данных' содержится ". ". awk '{sum +=. $col) END (print sum)' col=$colnum QFMT='%.4' $(l+"$@"} Параметр ${1+"$@"} содержит имена файлов (если они есть), введенные в командной строке, и решает проблему защиты специальных символов в интерпретаторе shell (46.07). Утилита awk суммирует значения в столбце, номер которого является значением переменной $col. По умолчанию сценарий выводит результат в целочисленном формате без десятичной точки. Встретив . (например, 1.5.), сценарий переключается на формат с плавающей точкой. Если для переменной OFMTустановлено значение %.4, утилита awk всегда выводит четыре цифры после десятичной точки. (Исходная установка переменной OFMT (%. б) обеспечивает вывод больших чисел в экспоненциальном формате. Если это то, что вам нужно, удалите выражение OFMT=' %. А f'.) - JP 49.08 Электронные таблицы — это здорово И Калькуляторы хороши, но нет ничего лучше электронных таблиц, когда необходимо выполнять сложные вычисления, а также изучать, сохранять и выводить результаты различных вычислительных сценариев. После долгих лет ожидания переноса в UNIX основных программ sc обработки электронных таблиц я открыл для себя программу sc. Поработайте с этой программой. Она понравится вам, хотя, может быть, и не сразу. Эта программа обладает набором мощных возможностей, однако имеет несколько неуклюжий пользовательский интерфейс и только схематичную документацию. К счастью, в программе есть экраны интерактивной справки, которые активизируются при нажатии клавиши с вопросительным знаком и позволят быстро освоить работу с программой (особенно если вы знакомы с электронными таблицами). Дадим вам несколько советов. Следите за подсказками, выводимыми некоторыми командами. Например, если в них приводятся аргументы в кавычках, то, скорее всего, вам также следует заключать их в кавычки. (Это не распространяется на квадратные скобки — признак необязательного аргумента.) Следите за порядком следования аргументов. (Например, команда копирования диапазона ячеек первым принимает диапазон назначения.) Одна из особенностей программы sc состоит в том, что команды редактирования ячеек построены на базе набора команд редактора vi (30.02). Возможно, это облегчит вашу работу или же сделает интерфейс еще более запутанным. Конечно, вы бы предпочли интерфейс, управляемый мышью, как в программе Excel, или клавишные меню, которые делают такой удобной работу с Lotus 1-2-3. Поверьте, программа sc также способна на многое. Она предоставляет почти все функциональные возможности, характерные для более дорогих программ работы с электронными таблицами: скрытые ряды и столбцы, символические имена ячеек и диапазонов, полный набор числовых, строковых и финансовых функций, а также функций даты и времени. В программе sc предусмотрены даже средства шифрования электронных таблиц с целью их защиты. Некоторые функции реализуются за счет организации каналов ом) к командам UNIX. Например, в программе sc отсутствует команда печати. Вместо нее применяются команды сохранения (Р — для сохранения файла в sc-формате; W— для сохранения экрана) с указанием канала к принтеру как к файлу. Это означает, что если вы знаете формат выходного потока (который достаточно прост), то сможете использовать любую доступную утилиту UNIX для преобразования данных. Программе sc нельзя передавать данные по каналу, однако вы можете подготовить данные с помощью других программ, а затем использовать программу psc, поставляемую совместно с sc, для преобразования их в гс-формат. - TOR 812 Часть девятая. Разное
49.09 Создание деловой графики с помощью программы ipl Одним из недостатков программы sc (49.0s) является отсутствие графических возможностей. Однако не следует отчаиваться. Программа ipl создает рисунки, используя язык PostScript (или SunView) для вывода. Она обеспечивает получение графиков и диаграмм различных видов, а также карт США и каналы. Программа предоставляет разнообразные команды форматирования (которые лучше изучать с помощью примеров, поставляемых в составе дистрибутива ipl). Предполагается, что данные расположены в полях, разделенных пробелами. Немного постаравшись, вы сможете получить эти данные из программы sc (и, конечно же, из многих других программ UNIX). - TOR
50 Справочная система 50.01 Интерактивная документация UNIX Операционная система UNIX — одна из первых систем, содержащих интерактивную документацию. Следует отметить, что эта документация не лишена недостатков: большинство пользователей ругают ее минимум раз в неделю. Однако она оказалась на удивление жизнеспособной. Интерактивная документация UNIX не является приложением к печатной документации. Она отличается полнотой, надежностью и обычно более актуальна, чем любой печатный документ. Ключевой командой системы интерактивной документации UNIX является команда man. В простейшем случае она выглядит следующим образом: % man тлил В качестве значения параметра тема обычно указывается имя некоторой команды, однако вместо него может быть задано имя системного вызова, библиотечной подпрограммы, устройства ввода-вывода или административного файла (или же название типа файла). Как правило, вывод команды man направляется программе постраничного просмотра, например more (25.аз). Разделы документации образуют несколько категорий. Существует три принципа распределения по категориям: один — для Berkeley-систем, другой — для System V и третий — для системы XENIX. Разделы документации перечислены в табл. 50.1. Таблица 50.1. Разделы документации UNIX Разделы Пользовательские команды Системные вызовы Библиотечные подпрограммы Ввод-вывод и специальные файлы Административные файлы Игры Разное Административные команды Служебные команды Локальные команды Устаревшие команды BSD 1 2 3 4 5 6 7 8 8 L (буква) о (буква) System V 1 2 3 7 4 6 или 1 5 8 1М * * XENIX u_man pjnan pjnan pjnan ajnan u_man любой раздел ajnan ujnan или ajnan В документации к System V и XENIX отсутствуют разделы, посвященные локальным и устаревшим командам. 814 Честь девятая. Разное
50.03 Как видите, в System V административные и служебные команды почему-то отнесены к разным категориям. Если вы поймете почему, сообщите нам, пожалуйста! Приверженцы Berkeley UNIX никогда не поймут этого. Некоторые из перечисленных разделов разбиты на более мелкие части. Можно, например, встретить разделы 3S (стандартная библиотека ввода-вывода), 1G (графика в Berkeley UNIX), IV (команды, разработанные для System V) и т.д. Чтобы просмотреть статью в определенном разделе документации, надлежит ввести одну из следующих команд: % man раздел тема % man -s раздел тема В некоторых системах необходимо указывать опцию -s (section — раздел). Например, если вы хотите прочесть документацию по файлу /etc/passwd (а не по команде passwd), введите такую команду: % man -s 4 passwd Таким образом можно различать темы с одинаковыми названиями, расположенные в разных разделах. - ML 50.02 Команда apropos Самая большая проблема в системе документации UNIX связана с поиском данных. Допустим, необходимо найти определенную строку в файле. Если вы не помните, что нужная для этого команда называется grep, то как узнать это имя? Решить данную задачу поможет команда apropos, эквивалент команды man -к (а иногда — псевдоним команды man -к). Правда, решение не всегда оказывается удачным, но это лучше, чем ничего. Команда просматривает заголовки всех разделов документации и выводит те из них, которые соответствуют заданному ключевому слову. Например, чтобы выяснить, как найти определенную строку, введите следующую команду: % apropos string gets, fgets (3S) - get a string from a stream getsubopt (3) - parse sub options from a string gettext, textdomain (3) - retrieve a message string, get & set text domain grep, egrep, fgrep (IV) - search a file for a string or regular expression puts, fputs (3S) - put a string on a stream Хотя для краткости мы отсекли часть вывода, из приведенного фрагмента видно, что для поиска определенной строки требуется команда grep или подобные ей команды. Все системы BSD и SVR4 поддерживают команду apropos. Однако существует множество систем SVR3 (и более ранних), которые не поддерживают ее. Если вы столкнетесь с подобной проблемой, обратитесь к параграфу 50.03. Напоследок ложка дегтя: с течением лет вывод команды apropos стал довольно запутанным. Теперь она считается не такой полезной, как десять лет назад. - ML 50.03 Имитация команды apropos в системах, где ее нет Меня попросили написать параграф о том, как сымитировать команду apropos в тех системах, где она отсутствует. Должен признаться, что никогда не сталкивался с подобной проблемой, но могу предложить ее решение. Ваше решение может быть другим, в частности из-за того, что в разных UNIX-системах используются различные способы хранения разделов документации. Справочная система 815
50.03 Итак, в соответствии с моим способом данная задача решается в два этапа. Во-первых, необходим сценарий, формирующий предметный указатель (это лучше, чем просматривать все разделы с помощью команды grep). [He всегда (s&ot). ;-) ->- JP\ Во-вторых, потребуется создать псевдоним с именем apropos, который автоматически ищет ваш файл предметного указателя. Вот сценарий для составления предметного указателя: #!/bin/sh С0) Л # manindex: генерирует список тем для поиска # с помощью команды grep. manindex # Затем создается псевдоним с именем 'apropos' и другие псевдонимы # для поиска согласно списку. # Этот сценарий следует запускать периодически — достаточно раз в месяц. mandir=/usr/share/man # каталог, в котором хранится документация raanlist="catl cat2 cat3" # список интересующих вас каталогов indexfile="$HOME/raanindex.txt" rra -f $indexfile for 9.12 for directory in $manlist do cd $raandir/$directory sed 34.24 * Команда sed превращает имена файлов в названия разделов документации, # например преобразует sed.l.z в sed. # ОШИБКА: неправильно обрабатывает такие имена, как a.out.4.z. '...' 9.16 for manpage in 4s I sed -e ' s/\. . *$//g' * do # Команда man используется для распаковывания раздела документации. „I 4jjg # Команда col предназначена для удаления липших символов. еягер27.05 # Команда egrep ищет пробелы, название раздела и дефис. uniq.tf.20 roan $manpage I col -b -x I egrep "Л +$manpage.* - " I uniq done done 45.23 done > $ indexf ile Этот сценарий обрабатывает все каталоги, в которых хранится документация. Он удаляет суффиксы из имен файлов, а затем использует команду man для вывода самого раздела документации. Это лучше, чем ориентироваться на просмотр необработанных файлов документации, поскольку многие разработчики их не поставляют. Если указать команде man найти какой-либо раздел руководства, то она всегда найдет его.* Команда col удаляет символы выделения полужирным шрифтом, символы подчеркивания и другие излишества, которые могут запутать команду grep- Наконец, команда egrep ищет строки, которые необходимо поместить в предметный указатель. Эта команда не так умна, как программа catman в BSD UNIX (которую имитирует данный сценарий), поэтому она находит много лишних строк, но нам кажется, что это незначительный недостаток. Прежде чем использовать наш сценарий, необходимо изменить значения следующих переменных: mandir Имя каталога самого верхнего уровня, в котором хранится документация. Часто таковым является каталог /usr/man, однако в вашей системе он может быть другим. manlist Список подкаталогов, в которых находятся интересующие вас разделы документации. Возможно, вы захотите включить в этот список хотя бы каталог, в котором хранятся описания пользовательских команд. В разных системах эти каталоги могут иметь различные имена. Мне кажется, что данный сценарий достаточно гибок, чтобы можно было учесть все возможные варианты. Если же это не так, немного измените его. Шаф!е Файл, в котором будет храниться предметный указатель (вывод данного сценария). Если у вас в системе используются исходные файлы документации, а не предварительно отформатированные, можете переписать сценарий, чтобы он искал непосредственно эти файлы. В данном случае поиск выполняется несколько быстрее. 816 Честь девятая. Разное
Ш5 Выполнение сценария manindex может занять несколько минут. Время его работы зависит от того, насколько полный указатель вы хотите получить: К счастью, нет необходимости часто выполнять данный сценарий: помимо начального запуска достаточно загружать его раз в месяц, чтобы учитывать "случайные" разделы документации, которые кто-нибудь может добавить. Хотите ускорить работу сценария? Ненужные разделы документации будут пропущены, если соответствующим образом задать переменную manlist. Например, если вас не интересует раздел 2, исключите из списка cat2. Имея в распоряжении предметный указатель, можно достаточно легко решить остальную часть проблемы. Самостоятельно создайте псевдоним (io.o2) с именем apropos и добавьте его в свой файл .cshrc: alias apropos "grep -i \!$ /horae/raike/manindex.txt" Вот как выглядит вывод этого псевдонима: % apropos search acctcom - search and print process accounting file(s) egrep = search a file for a pattern using full regular expression fgrep - search a file for a character string fmlgrep - search a file for a pattern grep - search a file for a pattern pathconv - search FMLI criteria for filename Я уже говорил, что предложенное решение не идеально. Но мне кажется, что оно является приемлемой заменой для команды apropos. - ML 50.04 Команда whatis: однострочное описание команды Команда whatis во многом подобна программе apropos (50.02), однако в качестве аргумента она требует имя команды, а не произвольную строку. Чем она полезна? Ну, скажем, вы забыли, каковы функции команды cat. В моей системе команда apropos cat выводит несколько экранных страниц информации. Правда, не хочется читать все? А вот команда whatis cat выводит симпатичное описание в одной строке: % whatis cat • cat (IV) - concatenate and display Если вы работаете с имитацией команды apropos {см. параграф 50.03), то можете сымитировать команду whatis с помощью псевдонима (ю.оз) или функции shell (Ю.щ: alias whatis "grep 'А *'\!$ /home/mike/manindex.txt" whatis () ( grep "" *$l'r /home/mike/manidex.txt; } 50.05 Команда whereis: определение местоположения файла Команда whereis помогает находить исполняемые файлы, исходные тексты и документацию, относящиеся к определенной программе. Я использую ее главным образом в профилактических целях. Если я ввожу команду more useless . txt и получаю сообщение more: command not found, то немедленно выполняю команду whereis more. Это дает мне много информации о существующих проблемах: возможно, кто-то. удалил из системы команду more, моя переменная среды PATH (6.04) установлена неправильно и т.п. Вывод команды whereis обычно имеет следующий вид: % whereis more more: /usr/ucb/more /usr/lib/more.help /u'sr/man/manl/more. 1 .. Это означает, что исполняемый файл команды — /usr/ucb/more, внутренний.файл справки — /usr/lib/more.hlp, а файл документации — /usr/man/manl/more.1. ■ Справочная система 817
50.06 Команда whereis имеет три опции, которые заслуживают внимания. Их назначение таково: -Ь — искать только файлы исходных текстов; -т — указать местоположение только раздела документации; -и — выдавать отчет только в случае отсутствия запращиваемой информации (исполняемых файлов, документации, исходных текстов). Существуют и другие опции, изменяющие список каталогов, в которых ведет поиск команда whereis. Если вы интересуетесь ими, просмотрите документацию. - ML 50.06 Просмотр разделов интерактивной документации Если другие описанные приемы не позволяют найти нужную информацию, просмотрите файлы интерактивной документации (so.oi). Возможно, вам придется перевернуть гору ненужной информации, однако ничего иного не остается. Рассмотрим пример. Предположим, вы помните о существовании команды для удаления столбцов из файла. Вы запускаете команду apropos (so.02), но она упоминает только команды colrm и рг, которые не подходят. Конечно, можно сузить диапазон поиска до одного-двух разделов документации (so.oi). В данном случае известно, что пользовательские команды находятся в разделе 1. Поэтому вы переходите в каталог с документацией и во всех файлах производите поиск слов column или chop ("отрубить, отрезать, удалить" — примеч. перев.) с учетом регистра: % cd /usr/man/manl % egrep -i 'column|chop' * awk.l:Add up first column, print sum and average: colrm.1:colrm \- remove characters from specified columns within each line cut.1:.IX cut "" "\fLcut\fP\(em remove columns from file" Это команда cut. Обратите внимание, что утилита awk также обрабатывает столбцы, однако команда apropos об этом не сообщает. (Я немного схитрил с этим примером: команду cut можно было найти другими способами, например путем поиска синонима к слову column (команда apropos field). Однако описанный метод работает и в более сложных случаях.) Чтобы выполнить поиск в файлах документации, необходимо знать, где они находятся. В каждой системе отведен свой каталог для файлов документации. Как правило в этих целях применяются каталоги /usr/man и usr/share/man. Если искомая команда является локальной, в первую очередь ищите ее в каталоге /usr/local/man и, возможно, в каталоге /opt (это очень большой каталог; для выполнения поиска используйте команду find (17.04)). Если в вашей системе есть быстрая команда find или locate (л.щ, попробуйте осуществить поиск по шаблону man или */man*. Возможно, вы найдете подкаталоги с такими именами, как man], man2 ... и (или) cat], cat2... Каталоги с именами типа manN содержат неформатированные файлы документации по разделу N. В каталогах catN находятся форматированные файлы документации. Вы можете обнаружить файлы с именами типа команда. N, где N равно 1 для первого раздела, 2 — для второго и т.д. Файлы документации делятся на два типа: неформатированные (см. параграф 50.11) и форматированные, с повторным выводом символов (см. параграф 43.18). С одной стороны, в неформатированных файлах легче вести поиск, поскольку слова не содержат управляющих символов. Как все это выполняется, показано в предыдущем примере. С другой стороны, неформатированные файлы содержат команды и макросы программы nroff (шз), что может затруднить поиск и чтение. Если планируется поиск в форматированных файлах, лучше избавиться от встроенных символов возврата. В противном случае команда grep может пропустить нужное слово, которое выделено полужирным шрифтом или подчеркнуто (так как оно содержит символы возврата). В следующем примере в цикле интерпретатора shell (».n, ч.щ к каждому файлу применяется некоторая последовательность команд. Первая команда, col -Ь (Ш8), удаляет управляющие символы, которые обеспечивают повторный вывод слов. Команда grep выполняет поиск 818 Часть девятая. Разное
50Л8 (с учетом регистра, как и прежде)- Поскольку эта команда читает стандартный ввод, ей не известно имя файла. Поэтому команда sed добавляет это имя перед каждой строкой, выведенной командой grep. $ cd /usr/man/catl * /./* $ for file in * > do col -b < $file | grep -i column | sed "s/A/${file):/" > done awk.l:Add up first column, print sum and average: cut.1:Use cut to cut out columns from a table or fields from each В интерпретаторе Bourne shell вывод выполняемых в цикле команд можно передать по каналу программе постраничного просмотра (например, less (25.M)). Для этого необходимо изменить последнюю строку цикла for. done I less - JP 50.07 Как UNIX-система определяет свое имя в сети Каждый компьютер в сети должен иметь имя. Во многих версиях UNIX это имя выводится командой uname -n. (GNU-версия данной команды помещена на компакт-диск. Программа инсталляции установит ее только в том случае, если ваша система имеет возможности для mime работы с ней.) В некоторых системах ту же функцию выполняет команда hostname или uuname -l (двойное и и строчное L). Если вы работаете с несколькими системами, команду hostname удобно использовать в строке припщшения интерпретатора shell, а также в том случае, когда вы забыли, в какую систему вошли. - JP 50.08 С какой версией я работаю? В последние годы команда which стала особенно популярной. Многие разработчики (например, Sun) отводят отдельные каталоги для команд, совместимых с BSD UNIX и System V. Какая версия команды будет работать у вас, зависит от того, какое вы установили значение для переменной среды PATH (в.му Во многих случаях важно знать, с какой версией ведется работа. Например, команда: % which sort /bin/sort сообщает, какая версия команды sort используется. (Так, в SunOS 4.1 применяется версия, которая совместима с BSD UNIX и находится в каталоге /bin, а не версия из каталога /usr/5bin, совместимая с System V.) Вы убедитесь, что команда which удобна во многих ситуациях. Вызывая эту команду, я всегда заключаю ее в обратные кавычки, чтобы получить полное путевое имя. Например, при написании параграфов для данной книги я начал с выяснения того, относятся ли команды man, apropos и whatis к одному и тому же исполняемому файлу. Это вопрос, над которым я никогда не задумывался. Вот один из простых способов найти ответ на него: % Is -li 'which man' 'which apropos' 'which whatis* 102352 -rwxr-xr-w 3 root 24567 Feb 8 1990 /usr/ucb/apropos 102352 -rwxr-xr-w 3 root 24567 Feb 8 1990 /usr/ucb/man 102352 -rwxr-xr-w 3 root 24567 Feb 8 1990 /usr/ucb/whatis О чем это говорит? Во-первых, три файла имеют одинаковый размер, следовательно, речь, скорее всего, идет об одном и том же файле. Кроме того, каждый файл имеет три ссылки, Справочная система 819
50M} а значит — три имени. Опция -i подтверждает это. Все три файла имеют одинаковый номер индексного дескриптора (1.22). Итак, команды apropos, man и whatis представляют собой один исполняемый файл, на который имеется три жестких ссылки. В некоторых реализациях System V команда which отсутствует. Версия этой команды, предоставляемая на компакт-диске, даже лучше, чем BSD-версия. Она не пытается читать файл .cshrc, чтобы выяснить, какие псевдонимы объявлены в нем. Вместо этого с помощью опции -/ задается чтение текущего списка псевдонимов интерпретатора shell. Это позволяет новой команде which показывать псевдонимы, определенные в командной строке и не помещенные в файл .cshrc. Данная команда работает также в интерпретаторах семейства Bourne shell (i.os), где поддерживаются функции (ю.щ. Чтобы заставить новую команду which читать текущий список псевдонимов, необходимо прибегнуть к одному трюку. Он состоит в следующем: создается псевдоним или функция shell, которые запускают команду which, передавая ей введенные определения (если таковые имеются) псевдонимов или функций. ^ Рассмотрим определение создаваемого псевдонима и дадим ему объяснение. Для интерпретатора С shell используется следующая строка в файле .cshrc: !\$ 10.03 alias which alias !\$ \| /usr/iocal/bin/which -i !\* V 10.03 (Функция с аналогичными возможностями помещена на компакт-диск.) Допустим, для того же примера создан псевдоним с именем sort, имеющий следующий вид: alias sort /usr/local/bin/quicksort Теперь, запустив команду which, вы получите: % which sort sort /usr/local/bin/quicksort Как работает эта команда? Интерпретатор передает указанный в командной строке псевдоним определенной вами команде which. В нашем случае выполняется следующая команда: alias sort I /usr/iocal/bin/which -i sort Первая часть команды (alias sort) передает по каналу определение псевдонима sort на стандартный ввод команды /usr/iocal/bin/which -i sort. Когда эта команда видит в своем стандартном вводе определение псевдонима, она выводит только его. А что если указать команде which найти команду, которая не имеет псевдонима? % which tr /bin/tr Интерпретатор shell будет выполнять такую команду: alias tr I /usr/iocal/bin/which -i tr Поскольку псевдонима с именем tr не существует, команда alias tr интерпретатора shell ничего не посылает в канал. Если команда /usr/iocal/bin/which -i tr не читает текст из стандартного ввода, она просматривает последовательность поиска и ищет первую команду с именем tr. Красивый трюк, не правда ли? Какой молодец Маартен Литмаат (Maarten Litmaath), автор этой программы! Это далеко не полный круг возможностей новой команды which. В сочетании с опцией -а данная команда показывает определение указанного вами псевдонима, а также ищет в вашей последовательности поиска все команды с тем же именем. Это удобный способ обнаружения всех существующих версий определенной команды. Завершим параграф примером, взятым из документации. Первая команда демонстрирует все псевдонимы (в данном случае — единственный псевдоним для новой команды which). Затем запускается новая команда which, определяющая, какую команду which мы выполняем :-); она показывает определение псевдонима с именем which. Третья команда, с опцией -а, перечисляет все имеющиеся версии команды which. —~——i » ,,,.,, . 820 Часть девятая. Раанеь В which И csh^init, shjait
50.09 % alias which alias !$ | % which which which alias !$ I % which -a which which alias !$ I /usr/local/bin/which /usr/ucb/which /usr/loqal/bin/which -i /usr/local/bin/which -i /usr/local/bin/which -i - ML, JP, MAL 50.09 Чтение предметного указателя с перестановкой ключевых слов Когда начинающие пользователи впервые просматривают системное руководство UNIX Reference Manual, они, как правило, удивляются документу необычного вида — предметному ■ указателю с перестановкой ключевых слов. Этот указатель выглядит примерно так, как показано ниже (приведён полный предметный указатель по трем командам: ar, at и awk). maintainer ar: archive and library '.....ar(l) ar: archive and library maintainer....ar(1) time at: execute commands at a later...at(1) processing language awk: pattern scanning and awk(l) at: execute commands at. a later time..*. at(l) at: execute commands at a later time..at(l) awk: pattern scanning and processing language.....;..... .awk(l) at: execute commands at a later time , at(l) ar: archive and . library maintainer ar(l) ar: archive and library maintainer ar(l) language awk: pattern scanning arid processing...awk(1) awk: pattern scanning and processing language . . : . .awk(l) awk: pattern scanning and processing language..awk(1) at: execute commands at a later time at(l) Как и само руководство UNIX Reference Manual, такой предметный указатель используется нечасто, однако иногда он оказывается чрезвычайно полезным. Чтобы найти нужную команду, просмотрите страницу и найдите интересующее вас ключевое слово справа от пустой разделительной полосы. Обнаружив ключевое слово, ознакомьтесь с кратким описанием команды, содержащимся в данной записи. Если сочтете целесообразным, можете прочесть раздел документации, который посвящен данной команде и указан в конце строки. Главное в руководстве UNIX Reference Мапиа1то,.что имя каждой команды является указателем на раздел документации, а сквозная нумерация страниц отсутствует. Это облегчает добавление разделов при появлении в системе новой команды. В то же время, если вы знаете имя команды, информацию о ней легко.найти, поскольку страницы упорядочены в алфавитном порядке, как в словаре. А как быть в том случае, когда имя искомой команды неизвестно? Если вы работаете за терминалом системы Berkeley UNIX, воспользуйтесь командой apropos (so.m), например: % apropos scanning awk(l) - pattern scanning and processing language Если же вы используете другую версию UNIX или работаете не за терминалом, вам больше подойдет предметный указатель с перестановкой ключевых слов. - TOR Справочная система 821
50.10 50.10 Как создать интерактивную документацию, не изучая программу troff Надеемся, вы пишете документацию для каждой команды, помещаемой в каталог bin. Обычно интерактивная документация (тля-страница) UNIX имеет следующий формат: NAME ' Имя программы — краткое описание ее работы. SYNOPSIS Описание способов вызова программы с перечислением всех аргументов и опций командной строки. (Необязательные аргументы помещаются в квадратные скобки.) DESCRIPTION Описание работы программы, если оно необходимо. OPTIONS Описание всех опций. EXAMPLES Один или несколько примеров использования программы. ENVIRONMENT Переменные среды, управляющие работой программы. FILES Файлы, которые программа читает и записывает. Могут быть указаны временные файлы. Здесь не указываются файлы, имена которых вводятся в командной строке. BOGS Замеченные ошибки в работе программы. В стандартном разделе документации обработке ошибок не уделяется достаточно внимания, однако такая информация была бы весьма полезна. AUTHOR Лицо, написавшее программу. Чтобы увидеть, как на самом деле выглядит /яая-страница, введите команду man Is. Вы можете пополнять такой документ любыми разделами по своему усмотрению. Один из путей получения красиво оформленной тая-страницы — применение макроса -man (so.ii) программы nroff (43.13). Однако программа nroff довольно сложна и ее применение для данной цели нельзя назвать целесообразным. Ограничьтесь созданием текстового файла, который по виду соответствует тому, что показан выше. Если вы работаете в BSD-системе и хотите отформатировать свою /шя-страницу с помощью программы nroff, найдите любой файл в каталоге /usr/man/manl и используйте его как образец. Совет: если вы решили отформатировать man-страницу с помощью макроса -man программы troff или groff, используйте программу nroffдля предварительного просмотра. Команда man (so.oi) эквивалентна следующей: more 2S.03 % nroff -в -man файл I more -в •ъ 25.10 Опцию -с команды nroff тл опцию -s команды тоге можно опустить. Помните, что программа nroff отсутствует во многих версиях System V UNIX, однако на прилагаемом компакт-диске есть программы gnroffn awf (43.17). Теперь новую /иая-страницу необходимо сделать удобочитаемой для стандартной команды man. BSD-системы позволяют достигнуть этого несколькими способами. Создайте в своем начальном каталоге подкаталог man, а в нем — подкаталог catl, после чего скопируйте в 822 Часть девятая. Разное
SO.ll этот подкаталог свою /иол-страницу, присвоив ей имя программа. 1 (где программа — имя новой команды). Чтобы просмотреть документацию, введите следующую команду: ~ 14.11 % man -M -/man программа Данную команду можно сократить. Мы строго придерживаемся правил наименования объектов, однако вы можете не создавать каталог man и поместить каталог call в свой начальный каталог. Тогда команда просмотра будет иметь следующий вид: % man -М ~ программа В некоторых системах есть переменная среды (6.ot) MANPATH, содержащая разделенный двоеточиями список каталогов, в которых должна вести поиск команда man. Например, моя переменная MANPATH содержит следующее: /home/mike/man:/usr/local/man:/usr/man Переменная MANPATH может оказаться более удобной в использовании, чем опция -М. Совет: рекомендуем поместить раздел документации в каталог catl, а не в каталог manl, поскольку программа man предполагает, что файлы в каталоге catl уже отформатированы. Если ваша программа рассчитана на совместное использование в системе, документацию по этой программе необходимо разместить в общедоступном месте. Зарегистрируйтесь в системе как суперпользователь и скопируйте свою документацию в каталог /usr/local/man/catl, назвав соответствующий файл программа.I ("1" означает "локальный"). Если вы не можете обладать правами суперпользователя, попросите системного администратора выполнить перечисленные операции. Сделайте свою документацию доступной для чтения всеми. Права доступа должны быть такими: % Is -1 /usr/local/man/catl -г—г—г— 1 root 468 Aug 5 09:21 программа.1 Это позволит команде man программа читать вашу документацию. В некоторых версиях System V правила иные. Структура страниц документации и самой команды man несколько отличается и не в лучшую сторону. Поместите тал-страницу в каталог doc. Затем создайте следующий псевдоним С shell (io.<0)\ alias myman "(cd ~/doc; man -d \!$ I pg)" или функцию (Ю.09): myman() { (cd $HOME/doc; man -d "$1" I pg); } Теперь команда myman аЬайя_документации выведет страницу документации. Обратите внимание: если в расширении отражен номер раздела (.1), нужно указывать полное имя файла {программа. /), а не только имя программы. Чтобы сделать тал-страницу общедоступной, скопируйте файл в каталог /usr/man/ujnan/manl, для чего понадобятся права суперпользователя. Сделайте файл доступным для чтения всеми пользователями. Если ваш раздел очень длинный, а место в файловой системе необходимо экономить, задайте сжатие документации посредством утилиты pack. Результирующий файл получит имя программа, l.z. Команда man автоматически распаковывает такой файл при чтении. - ML 50.11 Написание простой тап-страницы с помощью макроса -man Если вас не устраивает оформление простых страниц документации, о которых шла речь в параграфе 50.10, ознакомьтесь с нашими рекомендациями относительно разработки "настоящей" /иол-страницы. Мы уже отмечали, насколько удобно строить новую гаои-страницу на базе скопированной готовой страницы. Предлагаем простой способ такого копирования. Я не снабдил сценарий подробным объяснением. Вместо этого я включил в него множество комментариев, которые начинаются с символов Л" или \". Справочная система 823
Шп Л" Заголовок: имя программы, раздел документации, дата. I 50.0/ .ТН GRIND 1 "1992 October 12" ,\" Название подраздела: NAME (указывается имя команды и дается краткое описание), apropos S0.02 Л" Команда apropos ищет строку описания. .SH NAME "grind \- create output from input' Л" Название подраздела: SYNOPSIS (описание синтаксиса). .SH SYNOPSIS .В grind \" .В: полужирный шрифт для выделения имени команды. [ -Ь ] [ -с ] [ -d ] \" Необязательные аргументы заключаются в квадратные скобки. [ input [ output ]] \" Список аргументов может занимать несколько строк. .Ьг \" Подраздел SYNOPSIS заканчивается оператором конца строки (.Ьг). Л" Новый подраздел: DESCRIPTION (описание работы команды). .SH DESCRIPTION .1 Grind \" .1: Курсивный шрифт для слова "Grind". performs lofs of computations. Input to . IR grind , \" .IR: Одно слово курсивом, следующее — шрифтом roman \" без пробела между ними, is taken from the file . IR input , and output is sent to the file .IR output , which default to'standard input and standard output if not specified. . \" Следующий подраздел: теперь будут описаны опции -Ь, '^с и -d. .SH OPTIONS Л" Макрос . ТР предшествует описанию каждой опции. . .тр .В \-Ь Л" вывести -Ь полужирным шрифтом. Print output in binary. .ТР .В \-c \" \- - знак "минус", а не дефис. Eliminate ASCII-characters from input before processing. .TP .B \-d Cause daemons to overrun your computer. . \" Мы закончили описание программы и ее опций. Теперь приведем другие требования .\" (например, необходимые переменные среды и файлы) и описание возможных проблем. Л" При желании можно добавить другие подразделы. .SH FILES .PD 0 .ТР 20 .В /dev/null data file .ТР .В /tmp/grind* temporary file (typically 100 Megabytes) .PD .SH BUGS In order to optimize computational speed, this program always produces the same result, independent of the input. .\" Между абзацами используются макросы .LP. .LP If the moon is full, .1 grind may destroy your input file. To say nothing of your sex life. А" Хорошая документация заканчивается именем. автора программы., .SH A0THOR ' I wouldn't admit to this hack if my life depended on it. 824 Часть девятая: Разное
»4£ Изучив данный сценарий, вы наверняка заметили, что в нем использованы четыре основных /яои-макроса. В следующую таблицу включены описания этих макросов. Таблица 50.2. Основные тап-ммросы Макрос .ТН .SH .ТР .LP Назначение Заголовок раздела документации Заголовок подраздела Правильное форматирование описания опций (установка выступа) Признак нового абзаца подраздела Во всех разделах документации используются простые макросы .в, .BI и т.п., изменяющие шрифт. В примере я придерживался этого стиля, однако намного проще использовать встроенные команды изменения шрифта: \f I — курсив, \fB — полужирный шрифт и /fR — шрифт Roman. Возможно, существуют системы, в которых эти макросы не работают, но мне такие не попадались. - ML 50.12 Типичные сообщения об ошибках в UNIX Пользователи-новички расстраиваются из-за того, что сообщения об ошибках в UNIX часто бывают несущественными, краткими и непонятными. Такое сообщение, как Command not found (Команда не найдена), не требует объяснений. Но как понимать сообщение You don't exist. Go away. (Вы не существуете. Уходите.)? В настоящий параграф мы включили ряд типичных, а также загадочных или забавных сообщений, которые попадаются при работе в UNIX [или попадались. Во многих версиях UNIX система сообщений подверглась модификации. Например, вместо сообщения intruder alert! (Тревога! Нарушитель!) операционная система SunOS 4.1.3 теперь выводит No login associated with uid лит. (Нет учетной записи для пользователя с идентификатором num.). Жаль. : -) — JP\. Предлагаем вашему вниманию краткий список сообщений. Полный список мог бы занять целую книгу, а многие сообщения относятся к ситуациям, которые весьма трудно воспроизвести. arguments too long (Слишком длинные аргументы.) К сожалению, в UNIX существует ограничение на длину (количество символов) всех аргументов в командной строке. Изначально это количество было небольшим, но в последние годы оно увеличилось (5120 символов в SVR4). Тем не менее, проблема все еще остается нерешенной. Поэтому при вводе такой команды, как Is /*/*/*/* os.01), аргументы, скорее всего, окажутся слишком длинными. Как обойти эту проблему, рассказывается в параграфах 9.21 и 9.23. awk: bailing out near line л (Выход вблизи строки я.) Выполняется owfc-сценарий (зз.и), содержащий синтаксическую ошибку. Стандартная утилита awk чрезвычайно неинформативна в отношении синтаксических ошибок. Это сообщение является чуть ли не единственной подсказкой. Новые версии утилиты (например, nawk и gawk (ззм)) выводят более подробные сообщения об • ошибках. bad magic number (Плохое магическое число.) Это сообщение чаще всего появляется после запуска UNIX-команды. Кроме того, его может вывести программа Id (редактор связей для объектных модулей). Магическое число — это специальное число в исполняемом файле, на основании которого ядро определяет тип файла. Данное сообщение — признак того, что файл испорчен, предназначен для другой ОС либо вообще не является исполняемым. Справочная система 826
50.12 broken pipe (Разорванный канал.) Это сообщение появляется при выполнении конвейера, когда какая-либо его часть завершается прежде других. Предположим, выполняется команда % Is -1 | more Если программа ток завершится (по любой причине, например при вводе команды quit) до того, как она прочитает все данные, выведенные командой Is, получится "разорванный канал". bus error (core dumped) (Ошибка на шине (выведен файл дампа).) Ошибка того же рода, что и при нарушении сегментации памяти. Данное сообщение свидетельствует о том, что само ядро не выявило проблемы; это сделано подсистемой памяти (т.е. аппаратурой). Во многих UNIX-системах данное сообщение можно расценивать как признак неправильного выполнения операций ввода-вывода, доступа к несуществующему устройству и т.п. Какое отношение имеет подсистема ввода-вывода к памяти? Большое, но это выходит за рамки данной книги. команда: command not found (Команда не найдена.) Либо неправильно введено имя команды, либо оно не указано в последовательности поиска (S.o7). Если подобное сообщение выдается при использовании сценариев для shell, Perl, awk и т.д. с оператором #! (45.Ш), причину ошибки найти сложнее. Вы несколько раз проверяете имя команды и последовательность поиска; команда which (so.og) показывает, что команда присутствует в системе. Причиной ошибки может быть неправильное путевое имя интерпретатора, такое как #!/bin/hs в сценарии для интерпретатора sh. cross-device link (Ссылка на другое устройство.) Попытка создания жесткой ссылки на файл, который находится в другой файловой системе. Попробуйте создать символическую ссылку (is.04>. directory not empty (Каталог не пустой.) Попытка удаления непустого каталога посредством команды rmdir. Решить эту проблему довольно просто. Часто при просмотре каталог выглядит пустым. Однако в нем могут присутствовать скрытые файлы (i6.ii) (скорее всего, их имена начинаются с точки) или "следы" от ряда сеансов редактирования. Если вы абсолютно уверены, что ни один из этих скрытых файлов не является важным, введите команду гт -if (23.10, 23.17). intruder alert! (Тревога! Нарушитель!) Выводится командой whoami и является свидетельством того, что эта команда не может найти ваше имя в файле /etc/passwd (Зб.оз). Возможно, это означает, что кто-то сумел удалить или испортить этот файл. make: must be a separator on rules line 46 (Должен быть разделитель в строке 46.) Это проблема всех пользователей программы make (28.13). Строки правил программы make должны начинаться с символа [TAB]. Пробелы недопустимы. Появление сообщения об ошибке означает, что вместо символов табуляции введены пробелы. Некоторые редакторы (и, по сообщениям, некоторые сетевые утилиты) автоматически заменяют символы табуляции пробелами, поэтому даже если вы были внимательны и придерживались правил, все равно можете получить такое сообщение. not a typewriter (Это не печатающее устройство.) Кто-то сказал в Сети: "Черт возьми! Но это действительно не печатающее устройство. В чем же проблема?" Если серьезно, то это устаревшее сообщение, которое все еще появляется в некоторых случаях. Оно означает, что программа пытается выполнить операции ввода-вывода, которые были бы допустимы для терминала, но недопустимы при выводе в файл, канал и т.п. 826 Часть девятая. Разное
50.12 not enough memory (Мало памяти.) При выполнении программы недостаточно дискового пространства для подкачки (53.01). Заметим, что многие современные UNIX-системы позволяют системному администратору создавать новые файлы подкачки, благодаря чему можно избежать данной проблемы без повторного конфигурирования диска. Например, в SunOS указанная проблема решается с помощью команд mkflle и swapon. segmentation fault (core dumped) (Ошибка сегментации памяти (выведен файл дампа).) Эта ошибка вызвана тем, что программа попыталась выполнить запись или чтение по адресу памяти, который не принадлежит ей. Если у вас есть исходный код программы, проверьте, корректно ли присваиваются значения указателям. Если в вашем распоряжении нет исходного кода, то ничего нельзя поделать. Однако, скорее всего, программа получила неправильные входные данные — они оказались недопустимыми, превышающими заданный диапазон и т.п. typesetter busy (Наборное устройство занято.) Это сообщение я получил, работая с программой troff (43.12). Обычно оно означает, что неправильно указано устройство вывода, например не задана опция -/, обеспечивающая направление данных в стандартный поток вывода для дополнительной обработки каким-либо транслятором. На практике программа troff редко вызывается напрямую. Обычно это делается в сценарии. Если появилось подобное сообщение, значит, сценарий делает что-то неправильно. Как же все-таки трактовать это сообщение? Все очень просто. Программа troff по умолчанию генерирует выходные данные для фотонаборного устройства С/А/Т. Эти устройства устарели еще 10—15 лет назад, а сейчас, наверное, ржавеют в кучах металлолома. Но никто не удосужился изменить программу troff. Если не указать опцию -t, программа попытается вывести данные на наборное устройство. Когда это ей не удается (поскольку данного устройства нет), программа решает, что устройство "занято". who are you? (Кто вы?) Выводится командой Ipr (43.<В) и означает, что она не может найти ваше имя в файле /etc/passwd (З6.03). Это значит, что кто-то умудрился удалить или испортить этот файл. you don't exist. Go away. (Вы не существуете. Уходите.) Мне не приходилось видеть это сообщение, хотя оно является одним из мифов UNIX. Говорят, его выводит программа talk о.зз), когда ее пытаются запустить с устройства tty или (обычно) pt£ (41.щ, которое не значится в списке устройств, занятых зарегистрированным пользователем (этот список находится в файле /etc/utmp). Один из наших читателей получил такое сообщение, когда попытался перезагрузить свой компьютер после случайного удаления записи root в файле /etc/passwd 8-(. В большинстве случаев сообщения об ошибках являются стандартными, однако некоторые разработчики добавляют свои сообщения. Некоторые разработчики попытались защитить нас от откровенных оскорблений. Я надеюсь, что все системы, в которых содержится легендарное сообщение об ошибке Don't you hate obscure messages (Спокойно относитесь к непонятным сообщениям), канули в Лету. - ML Справочная система 827
51 Полезные программы и забавные случаи 51.01 Несколько программ под занавес Ну, вот и приехали! Перед вами глава о "разном" в части "Разное" в книге о всякой всячине! Эта глава содержит парафафы, которые мы удалили из других глав, но от которых не захотели отказаться. Некоторые парафафы не подошли ни к одной главе, другие вполне можно было включить в одну из глав, но они поступили слишком поздно. Если бы мы попытались вставить их в предыдущие главы, это вызвало бы нервную дрожь у наших верстальщиков. - TOR 51.02 Как UNIX вычисляет текущее время Как и другие операционные системы, UNIX имеет свою подсистему вычисления времени. И почти все UNIX-системы, даже самые маленькие, содержат аппаратные часы со встроенной резервной батарейкой. Все UNIX-системы определяют время, подсчитывая количество микросекунд, прошедших с полуночи 1 января 1970 года по Гринвичу. Эту дату обычно называют эпохой (epoch) и считают началом эры UNIX. Хотя связанные с UNIX работы начались на заре 60-х, первые версии системы стали доступными (в Bell Laboratories) в начале 70-х. Подсчитываемое число обновляется примерно 60 раз в секунду. Точное значение зависит от реализации UNIX-системы и задается . крнстантбй HZ, определенной" в -файле /usr/in- cluae/sys/param.h: #define HZ 60 Это временное разрешение; часто его называют /пактом. Заметим, что такт не имеет никакого отношения к рабочей частоте системных часов. Точность измерения времени обычно не выше, чем точность.системных часов, хотя некоторые системы располагают дополнительными средствами, обеспечивающими более точное измерение. Если ваша UNIX-система работает в сети, офомное значение приобретает синхронизация всех системных часов. Иногда при копировании файлов из одной системы в другую происходят странные вещи — время создания копии относится к будущему. Во многих UNIX-системах запускается демон времени (Одна из загадочных вспомогательных программ (/.14)), который синхронизирует системное время для машин в сети. ОС UNIX автоматически отслеживает переходы на зимнее и летнее время, високосные годы и прочие нюансы хронологии. Установив систему, вы должны предоставить ей сведения о часовом поясе и правилах перехода на летнее и зимнее время. Поскольку UNIX стала международным стандартом, значительно увеличилось количество часовых поясов, которые необходимо указывать в ней, а также правил перехода на зимнее и летнее время, которые надлежит соблюдать. В некоторых случаях все эти установки приходится выполнять вручную. 828 Часть девятая. Разное
51.03 Например, в Великобритании даты перехода на летнее и зимнее время могут изменяться из года в год, поскольку они ежегодно устанавливаются парламентом по предложению Военно- морского ведомства. Внутренние программы UNIX вычисляют время относительно начала эпохи, однако вам не стоит беспокоиться об этом, если вы не программируете на С. Благодаря библиотеке подпрограмм по работе со временем можно выполнять преобразование из внутреннего формата в другие форматы представления времени. Более подробную информацию вы найдете в разделе документации UNIX ctime(S). - ML 51.03 ASCII-символы и их коды Многие UNIX-системы поставляются вместе с файлом ascii, который находится в каталоге /usr/pub или /usr/share/lib/pub. Этот файл содержит список ASCII-символов с восьмеричными и шестнадцатеричными кодами для каждого символа. Приводим две строки из раздела восьмеричных кодов: |030 сап|031 em 1032 subl033 esc 1034 fs |035 gs 1036 rs 1037 us | 1040 sp |041 ! |042 " 1043 # 1044 $ 1045 % 1046 & 1047 ' | Мы видим, что символу [ESC] соответствует восьмеричный код 033, а символу процента (%) - 045. Если в вашей системе подобный файл отсутствует, его можно создать самостоятельно, что не займет много времени. Для этого следует воспользоваться таблицей ASCII-символов или просмотреть /яви-страницы ascii(7) или ascii(5). Вы можете создать свою версию таблицы, заменив в ней такие имена, как sub, обозначениями клавиш, при нажатии которых вводится соответствующий символ (в данном случае — Az). Если в вашей UNIX-системе не используется кодировка ASCII, разработайте аналогичный файл для используемой кодировки. При наличии файла ascii сценарий с именем ascii может найти восьмеричный код символа. Например: % ascii esc 033 % ascii a 141 % ascii \S . 046 He забывайте защищать специальные символы (S.n), как в примере с амперсандом: #!/bin/sh file=/usr/pub/ascii # Создание шаблона, структура которого совпадает gffjj # со структурой файла $file case 44.06 case "$1" in П\\/]> pat=" \\$1 " ;; # позволяет искать символы [, \ и / ?) pat=" $1 " ;; ??) pat="$l » ;; ???) pat="$l" ;; *) echo "Usage: "basename $0" char (char must be single character like 'a' or name like 'soh')." 1>&2 exit 1 esac \{..\).\l 34.10 sed -n "1,/Л\$/з/.*|\(Ю-9] [0-9] [0-9]\) $pat | .*/\l/p" $file Сценарий формирует выражение для редактора sed (34.24), которое позволяет создать шаблон, совпадающий с первым разделом файла ascii (до пустой строки, предшествующей разделу с Полезные программы и забавные случаи 829
51.04 шестнадцатеричными кодами). Например, команда ascii а записывает в переменную pat значение ПаП, вследствие чего команда замены редактора sed приобретает следующий вид: s/ | пппППаП | /ллл/р где символ □ соответствует пробелу, a nnn — восьмеричному коду символа а. Команда jot (4s.ii) также переводит числовые коды в соответствующие символы. Однако она не показывает названий непечатаемых символов (например, esc). - JP 51.04 Кто в системе? Команда who выводит список пользователей, которые в настоящий момент зарегистрированы в системе. Вот пример ее вывода в моей системе: % who naylor ttyZl Nov 6 08:25 hal ttypO Oct 20 16:04 (zebra.ora.com:0.) pmui ttypl Nov 4 17:21 (dud.ora.com:0.0) jpeek ttyp2 Nov 5 23:08 (jpeek.com) hal ttyp3 Oct 28 15:43 (zebra.ora.com:0.) Каждому терминалу или окну посвящена отдельная строка. Столбцы содержат такие данные: имена пользователей, зарегистрировавшихся в системе, номера портов ff}> (з.щ и время регистрации. Если подключение осуществлялось через сеть (из), наряду с перечисленными данными указывается адрес пользователя в скобках. Например, пользователь hal зарегистрировался дважды. В системах со многими пользователями вывод команды who удобно просматривать с помощью команды grep (27.01). Например: % who I grep "Ahal " ... через какой порт зарегистрировался пользователь hal? % who I grep "Uov 6" ... кто зарегистрировался сегодня? -v 27.03 % who I grep -v "Uov 6" ... кто зарегистрировался не сегодня? GNU-версия команды who, предоставляемая на компакт-диске, обладает большими возмож- (S) J ностями, чем многие другие версии. who 51.05 Регистрация выполняемых команд с помощью программы script Вам необходимо ввести сложный набор команд, чтобы показать его кому-то или сохранить в файле для включения в документацию? Вы столкнулись с тем, что программа, которую вы отлаживаете, в каком-то месте работает неправильно, но сообщение об ошибке мелькает так быстро, что его не удается прочесть? Может быть, вы хотите показать предварительно записанную демонстрацию интерактивной программы? Программа script поможет решить все эти задачи. Применение: Версии программы script в UNIX-системах, не поддерживающих псевдотерминалы (pty) (41.08), не являются столь гибкими, как описанная здесь версия. Например, такие версии не позволяют управлять заданиями (izoi) во время выполнения сценария. Чтобы скопировать все выполняемые команды в файл, введите: % script Script started, file is typescript 830 Часть девятая. Разное
51.06 Теперь по приглашению интерпретатора shell можно вводить любые команды. Все, что выполняется, копируется в файл typescript текущего каталога. (Чтобы использовать другое имя файла, введите его путевое имя (Ш) в командной строке, например script файл.) Завершив запись, нажмите клавиши [CTRL-d] или введите команду ехЦ (38М) по приглашению интерпретатора shell. Многих удивляет то, что сценарий, создаваемый программой script, записывает все, в том числе Escape-последовательности (s.osj, посылаемые программами на терминал. Это и хорошо, и плохо. Хорошо то, что можно воспроизвести события, выводя сценарий на экран с помощью команды сШ (25.02). Во время выполнения программы script запустите такую интерактивную программу, как v/, а затем выйдите из программы script и воспроизведите сценарий с помощью команды cat typescript. Курсор "забегает" по экрану, а файл будет повторно отредактирован на ваших глазах. (За этим процессом легче наблюдать, если для терминала задана небольшая скорость передачи данных.) Плохо то, что команды исправления ошибок и управляющие последовательности терминала также будут присутствовать в создаваемом файле. При редактировании или печати в файле сценария окажется много лишнего, например символы ЛМ (возврат каретки) и ЛН (стирание). (Команда cat -v или od -с (25.07) выюдит на экран подобные символы.) Если файл содержит лишь несколько таких символов, их несложно удалить вручную, выполнив в текстовом редакторе глобальную замену. "Чистку" сценария можно автоматизировать, используя приемы, которые описаны в параграфах 35.11, 43.18 и 51.06. - JP 51.06 "Чистка" файла сценария Как указано в параграфе 51.05, файлы, создаваемые программой script, могут содержать произвольные управляющие символы. Сценарий script.tidy позволяет очистить файл от таких символов. Данный сценарий написан Дэном Бернстайном (Dan Bernstein) и опубликован в Usenet. Я внес в него некоторые изменения. Сценарий читает данные из файла или стандартного ввода и записывает их в стандартный поток вывода. В сценарии script.tidy используется редактор sed (34.24) для удаления символов [GTRL-m] ([RETURN]) в конце строк. Кроме того, применяется команда tes/ (шо), с помощью которой повторяются последовательности команд, удаляющие символы после символов [CTRL-h] ([BACKSPACE]). Если для активизации команды удаления (5М) вы пользуетесь клавишей [DELETE], измените сценарий так, чтобы он удалял символ [DELETE], а не [BACKSPACE]. В сценарии реализован трюк (45.35), в котором задействованы команды echo и№и который предназначен для сохранения управляющих символов в переменных интерпретатора shell. Поскольку serf-сценарий заключен в двойные кавычки (Ш), перед запуском редактора sed интерпретатор shell выполняет подстановку переменных в нужных местах. #!/bin/sh # Символ [CTRL-m] помещается в переменную т, # а символ [CTRL-h] — в переменную Ь. # Если для стирания введенного символа вы используете клавишу [DEL], # вместо \010 введите \177. eval "echo m=M b=H I tr 'MH' '\015\010|4 exec sed "s/$m\$// : x s/r$b]$b// t x" $* iSW-сценарий, содержащийся в файле script.tidy, можно модифицировать таким образом, чтобы он удалял ряд Escape-последовательностей (.ч.ов), характерных для вашего терминала. В параграфе 41.11 показано, как найти такие последовательности. ("Настоящий" сценарий script.tidy, работающий в автоматическом режиме, должен читать соответствующие записи в базах данных termcap и terminfo, а затем искать правильные Escape-последовательности в файле.) - JP ш script.tidy eval 8.10 exec 45.07 Полезные программы и забавные спучаи 831
51.07 51.07 Когда не хватает терпения о Иногда вы замечаете, что снова и снова повторяете одну и ту же последовательность команд (например, последовательность команд ps ps.os) для наблюдения за протеканием фоновых процессов или команд 1р£ (43.<в), позволяющих узнать, закончилась ли печать). Вместо того чтобы повторять ввод одной и той же команды или использовать перечень ранее введенных команд (11.0/) интерпретатора С shell, воспользуйтесь командой vis. Например: % vis ps Эта команда отображает на экране вывод команды ps. Выполнение команды ps и вывод обновленной информации производятся каждые 15 секунд. Если такой интервал кажется вам слишком большим, с помощью опции -d установите для команды vis меньшую паузу: % vis -d 2 ps Теперь информация будет обновляться каждые 2 секунды. При этом экран очищается, и на нем появляется вывод команды ps. В первой строке команда vis указывает имя выполняемой команды, продолжительность паузы {если она нестандартная) и количество вызовов команды. Значение в поле Exec: инкрементируется при каждом повторении команды. Exec: 1 Command: PID ТТ 2971 pi 6139 pi 614 5 pi 3401 qO 5954 qO 14019 q5 29380 r7 29401 rd ps STAT S S R IW s IW IW IW TIME 0:06 0:00 0:00 0:13 0:01 0:02 0:00 0:00 Delay COMMAND -sh (csh) vis -d 2 ps ps -sh (csh) vi chOl -sh (csh) -bin/csh (csh) -bin/csh (csh) Команда vis имеет ряд других опций командной строки. Опция -rs особенно полезна — она обеспечивает выделение всех строк, изменившихся с момента последней итерации. Заметим, что в общедоступных сетевых архивах появлялись варианты этой команды под разными именами — display, rep, watch и т.д. Итак, надеемся, что вы убедились, насколько удобна команда vis. - LM 51.08 Вводите выстрел и звездочку и не забывайте о кроличьих ушках Чтобы люди считали вас мудрым, богатым и сильным, совсем необязательно таковым быть — достаточно притвориться. То же относится и к UNIX. Если вы станете разговаривать как специалист, люди подумают, что вы на самом деле им являетесь. Знатоки UNIX пользуются особым секретным шифром для обозначения часто употребляемых спецсимволов. Поскольку умение правильно говорить является эффективным средством воздействия, : -) приводим краткое руководство для хакеров. Символ 1 1) * 1 Стандартный термин Восклицательный знак (exclamation point) Кавычка (quotation mark), двойная кавычка (double quote) Знак сноски (asterisk) Апостроф (apostrophe), одинарная кавычка (single quote) Точка (period) "Эффектный" термин Выстрел (bang), визг (shriek), бита (ball-bat) Кроличьи ушки (rabbit ears) Звездочка (splat), звезда (star) Птичка (tick) Пятно (dot), пункт (point) 832 Часть девятая. Разное
51.09 Если этого недостаточно, чтобы вызвать отвращение... прошу прощения, чтобы удовлетворить ваше любопытство, найдите копию "Словаря хакера" Эрика Реймонда (Eric S. Raymond). Он создан на базе файла жаргонизмов — сетевой коллекции словечек хакеров, которую в World Wide Web можно найти, по адресу http://mm.ccil.org/Jargpn.html (на момент подписания книги в печать данный адрес был действительным). Первоначально файл жаргонизмов формировался как список терминов, используемых программистами группы искусственного интеллекта в Массачусетском технологическом институте, Стэндфордском университете и университете Карнети-Меллоун. С тех пор он разросся и сегодня включает многие термины, бытующие в сообществах Internet и UNIX. — IP 51.09 Создание регистрационного интерпретатора shell При входе в большинство UNIX-систем текущий интерпретатор shell является регистрационным. Такой интерпретатор имеет свои особенности. Например, он читает специальные файлы конфигурации (2.<в), такие как .profile и .login. ОС UNIX знает, как сообщить интерпретатору shell о том, что он должен выполняться как регистрационный. Если ввести имя интерпретатора shell (например, sh или /bin/csh) в командной строке, то он не запустится как регистрационный. Иногда при проверке конфигурации учетной записи или во время работы в оконной системе требуется запустить регистрационный интерпретатор без входа в систему. В UNIX интерпретатор shell работает как регистрационный, если во время запуска перед его именем был поставлен дефис (-).* Проще всего запустить регистрационный интерпретатор, создав копию интерпретатора shell и присвоив ей имя, начинающееся С дефиса (при этом тратится много дискового пространства; кроме того, данный прием невозможно реализовать, если для интерпретатора установлена защита от чтения): bin 4.02 $ cd $HOME/bin ./-23.14 $ ср /bin/csh/ ./-csh Лучше создать символическую ссылку иш) на интерпретатор shell: $ cd $HOME/bin $ In -s /bin/csh/ ./-csh (Применение жесткой ссылки (is.04) допускается, если ваш персональный каталог bin находится в той же файловой системе, что и каталог /bin.) Третий способ сводится к написанию небольшой программы на С (S2.es), которая запускает интерпретатор shell, сообщив ему, что его имя начинается с дефиса: main () { execl ("/bin/csh", "-csh", 0); } Независимо от того, какой способ выбран, вы можете запустить новый интерпретатор shell, набрав его имя: $ -csh .., регистрационный интерпретатор С shell % ... запуск любых команд % logout $ ... снова в исходном интерпретаторе shell В параграфе 2.16 показано, как использовать этот прием для замены обычного регистрационного интерпретатора shell. - JP Интерпретатор bash имеет опцию командной строки -login, которая заставляет его работать подобно регистрационному интерпретатору. Полезные программы и забавные случаи Я »-171 833
sue 51.10 Программа date Все UNIX-системы поставляются с программой date. Она показывает время согласно системным часам (si.a). (Следовательно, время будет настолько точным, насколько точны системные часы.) Например: % date Fri Dec 13 08:06:39 PST 1996 Если вам нужны составляющие даты, разделите выведенную строку на части. В параграфах 16.18, 21.03 и 2.05 приведены соответствующие примеры. Новые версии программы date (в том числе версия на прилагаемом компакт-диске) позволяют устанавливать формат вывода даты. Для этого вводится единственный аргумент, который начинается со знака "плюс" и содержит символы описания формата. Список этих символов можно найти в интерактивной документации. Аргумент должен представлять собой единое слово, поэтому обычно его необходимо заключать в кавычки (t.u). Приведем простой пример: % date +'Today is %d %h 19%y.' Today is 13 Dec 1996. Чаще всего подобный формат даты используется в сценарии или в определении псевдонима (Ю.02). Можно извлечь пользу из способности программы date выводить как дату, так и любой текст. Грег Уббен применяет эту команду для конструирования арифметических выражений с датой. Например, поскольку команда date +%U допустима не во всех UNIX-системах, ее можно заменить выражением, приведенным ниже. Это выражение вычисляет номер недели в году (считается, что первая неделя года начинается с первого воскресенья) и присваивает его переменной $weekno в качестве значения: ехрг4S.2S weekno='expr Vdate +"%j / 7 + ( ( %w + 6 ) %% 7 < %j %% 7 )"\" Y...V 4S.31 Другие примеры вы найдете в параграфах 16.16, 18.03 и 21.14. - JP 51.11 Создание файла произвольного размера для тестирования Команда yes <23.04> выводит текст указанное число раз. Если для тестирования вам нужен файл определенного размера, его можно создать с помощью команд yes и head (25.20). Например, чтобы создать файл размером 100 Кб (102400 символов), содержащий 12800 строк по 8 символов (7 цифр и символ новой строки), введите следующее: % yes 1234567 | head -12800 > 100k-file Примечание: В некоторых UNIX-системах эта команда может зависнуть. Тогда ее необходимо уничтожить с помощью команды [CTRL-c], поскольку команда head продолжает читать данные из канала. Если такое произошло в вашей системе, замените команду head -12800 командой sed 12800q. — JIK, JP, из телеконференции сотр.unix.questions в Usenet, // ноября 1991 г. 51.12 Вам не хватает улыбок? Вы наверняка встречали в электронной почте (из) (и в этой книге!) значки выражения эмоций — улыбающиеся рожицы. Если же вы не видели этих значков, отметим, что они напоминают те ужасные желтые пуговицы с изображением улыбающегося лица, которые были модными в начале 70-х. Значки, о которых идет речь, печатаются боком, например: : -). (Поначалу, чтобы разглядеть лицо, вам придется наклонять голову, но постепенно вы 834 Часть девятая. Разное
51.12 привыкните воспринимать их и так.) Стандартный знак улыбки, приведенный выше, означает: "Не принимайте это всерьез". Есть также знак нахмуренного лица, свидетельствующий о том, что вас что-то расстроило: :-(. А что означает 8-0? Это следует трактовать как "О, боже!" (используется после выполнения команды rm -rf *). ^^^| Значки для выражения эмоций могут быть разными: от полезных до абсурдных. Дэвид I (9) | Сандерсон (David Sanderson), которого газета Wall Street Journal назвала "энциклопедистом k»_-^ значков эмоций", собрал сотни таких значков и поместил их в программу smiley, smiley Если вызвать эту программу без аргументов, она выведет произвольно выбранный значок эмоций с объяснением: % smiley 7:Л] Ronald Reagan Введите команду smiley со значком эмоций в качестве аргумента, и вы получите все возможные объяснения (а также дополнение к объяснению, если оно имеется). Поскольку большинство значков состоят из специальных символов, не помешает заключить их в кавычки (в.ы, s.isy. % smiley ';-)• ;-) "If you touch my daughter again, they won't be blanks" [RICHH] (Еще раз тронете мою дочь - пожалеете.) beaten up (меня побили) could be pirate smiling face?? (могут ли пираты улыбаться?) crying with happiness (смех сквозь слезы) getting fresh (свежая идея) sardonic incredulity (сардонический скептицизм) smiling face gets his lights punched out (улыбающееся лицо с подбитым глазом) winking (подмигивание) Если ввести smiley -1, будет выведен список всех известных значков. В параграфе 7.13 показано, как заставить интерпретатор bash помещать различные значки в строку приглашения. И если вы получаете мало улыбок по сети, загляните в небольшую книгу Smileys Дэвида Сандерсона (опубликована издательством O'Reilly & Associates). - TOR, JP Полезные программы и забавные случаи 27* 835
52 Что находится на компакт-диске? 52.01 Введение Прилагаемый компакт-диск содержит исходные тексты и двоичные файлы программ, которые описаны в книге. На диске находятся две группы файлов: • Свободно распространяемые программы, которые нам понравились и о которых мы упоминали в данной книге. Большинство этих программ написано на С и распространяется как в исходном, так и в двоичном формате. Мы скомпилировали двоичные файлы для всех платформ, перечисленных в параграфе 52.03, а также полностью проверили программы для систем Red Hat Linux и Digital UNIX. • Написанные или адаптированные нами сценарии, которыми мы хотим поделиться с читателями. Отбирая свободно распространяемые программы для компакт-диска, мы пытались руководствоваться соображениями качества, а не количества. Многие архивы программного обеспечения перегружены всем, что сумели найти их авторы, совершенно не интересуясь ценностью найденного.. Приходится просматривать все подряд и выяснять, чем стоит пользоваться, а чем нет. ■Г"^И Каждая программа и каждый сценарий на компакт-диске, по крайней мере, упомянуты в [ (gi 1 книге. В том месте, где программа упоминается впервые или где приведена очень важная р-_-4 информация о ней, мы помещали соответствующую пиктограмму и название продукта. Это пример делалось для того, чтобы вы могли прямо в процессе чтения инсталлировать заинтересовавшую программу (как описано в параграфе 52.05) и добавить ее в свою коллекцию инструментальных средств. (Конечно, приступая к чтению книги, вы можете инсталлировать сразу все программы.) Настоящая глава содержит такую информацию: • краткий обзор содержимого компакт-диска (параграф 52.04); • версии UNIX, для которых программы уже скомпилированы (параграф 52.03); • подробное описание процедуры инсталляции (параграф 52.05); • инструкции по установке дисковода компакт-дисков (параграф 52.06); • советы относительно того, как получить программное обеспечение из сетевого архива (файлы, помеченные звездочкой в параграфе 52.04; инструкции приведены в параграфе 52.07), а также на альтернативных носителях; • рекомендации, как построить программу из исходного текста, если вы не работаете на одной из поддерживаемых платформ (параграф 52.08); • пути получения консультаций по работе программы, а также по ее переносу на другую платформу (параграф 52.09). - TOR 836 Часть девятая. Разное
52.03 52.02 Свободно распространяемые программы для UNIX Может оказаться, что некоторые программы уже есть на вашем сервере. В конце концов, ОС UNIX является коллекцией свободно распространяемых программ (l.oi). Вот лишь несколько программ, которые когда-то были подарены UNIX (большинство — Калифорнийским университетом для BSD-версии) и стали стандартными компонентами операционной системы: • интерпретатор С shell (l-osy, • редактор v; (зо.огу, • база данных termcap (s.t>2). А вот некоторые другие программы, используемые повсеместно (и являющиеся официальными составляющими некоторых, но не всех, версий UNIX): • RCS (20.14); • ^2 (24.07); • patch (33.09); • Emacs (32.oiy, • Ш (35.14) И paste (3S.18). Мы хотели, чтобы вы имели в своем распоряжении все великолепные программы второй группы, поэтому на всякий случай поместили их на компакт-диск. — TOR 52.03 Коммерческое программное обеспечение для UNIX Одним из весомых достоинств ОС UNIX всегда была переносимость. Она может работать на большем количестве аппаратных платформ, чем любая другая операционная система: нет такой распространенной платформы, которая не поддерживала бы ту или иную версию UNIX. К сожалению, это достоинство UNIX в то же время является ее недостатком. В связи с тем, что UNIX способна работать на многочисленных конкурирующих аппаратных платформах, невозможно пойти в магазин программного обеспечения, купить приложение UNIX, принести домой и установить его на своем компьютере. Для UNIX вряд ли появится такое коммерческое программное обеспечение, которое предлагается для операционных систем MS Windows и Macintosh. Бесплатные программы для UNIX обычно распространяются в виде исходных текстов. Поэтому сфера их применения ограничена: в роли "потребителей" выступают программисты, которые знают, как построить исполняемый файл из исходного. Приложения продаются для конкретной платформы. Вы должны сообщить поставщику программного обеспечения тип своей машины, чтобы он мог предоставить вам ленту с программами, скомпилированными для машин данного класса. С появлением компакт-дисков большой емкости ситуация в корне изменилась. Поскольку один диск имеет емкость более 600 Мб, мы смогли поместить на него не только исходные тексты всех программ, но и предварительно скомпилированные двоичные файлы для семи самых распространенных UNIX-платформ: • Red Hat Linux 4.1; • Sun4 Solaris 2.5; • Sun4 SunOS 4.1.4; • Digital Equipment Corp. 3.2; • IBM RS/6000 AIX 3.2.5; • HP700HP-UX9.il; • SCO UNIX 3.2.4. Если вы работаете на одной из этих платформ, вам нужно только смонтировать компакт-диск в своей системе и запустить программу инсталляции. Эта программа достаточно разумна, Что находится на компакт-диске? 837
52.04 чтобы определить тип вашей платформы и загрузить правильные версии программ (подробнее — в параграфе 52.05). Если же ваша платформа не относится к поддерживаемым, то все равно остается возможность построения программ из исходных файлов с помощью специального сценария, который мы также предоставляем (подробнее — в параграфе 52.08). - TOR 52.04 Краткое описание содержимого компакт-диска В книге приведена информация обо всех программах, находящихся на компакт-диске. Эти места обозначены пиктограммой на поле страницы. Здесь мы приводим краткое описание работы каждой программы с указанием места в книге, где говорится о ней. Если у вас нет дисковода компакт-дисков или вы хотите получить программы другим способом, ставим вас в известность, что многие из них доступны по Internet или электронной почте. Рядом с именами таких файлов стоит звездочка. В параграфе 52.07 рассказано, как получить программы по сети. Многие из перечисленных здесь программ представляют собой сценарии shell. Теоретически все сценарии являются переносимыми с одной платформы на другую, однако их работа может зависеть от того, инсталлированы или нет определенные программы на данной платформе. Кроме того, сценарии могут вести себя не так, как предполагал разработчик. Поэтому будьте готовы к тому, что, возможно, придется немного подправить сценарии, чтобы они правильно работали в вашей системе. Следует также учитывать, что в некоторых сценариях, написанных на языках awk, sed и perl, в начале первой строки стоит оператор #!, сообщающий системе, какой интерпретатор запустить. Не все версии UNIX поддерживают этот синтаксис. Если ваша система не поддерживает его, необходимо преобразовать такой сценарий в сценарий shell. Дополнительную информацию вы найдете в параграфах 45.03 и 45.04. .emacs_ml * Файл .emacsjnl содержит перечень любимых Майком Лукидисом команд редактора Emacs. Если они вам понравятся, поместите их в свой файл $HOME/.emacs (см. параграф 32.07). .enter.csh * Файл .enter.csh является примером сценария С shell, который можно автоматически запускать при переходе в определенный каталог. Предполагается, что он будет использоваться с псевдонимом (находится в файле cshjnit), а также со сценарием .exit.csh, который также помещен на компакт-диск (см. параграф 14.14). .enter.sh * Файл .enter.sh является примером сценария Bourne shell, который можно запускать при переходе в определенный каталог (в интерпретаторах sh, ksh и bash). Предполагается, что он будет использоваться с функцией (находится в файле shjnif), а также со сценарием .exitsh, который также помещен на компакт-диск (см. параграф 14.14). .exit.csh * Файл .exit.csh является примером сценария С shell, который можно запускать при выходе из определенного каталога. Предполагается, что он будет использоваться с псевдонимом (находится в файле cshjnit), а также со сценарием enter.csh, который также помещен на компакт-диск (см. параграф 14.14). .exitsh * Файл .exit.sh является примером сценария Bourne shell, который можно запускать при выходе из определенного каталога. Предполагается, что он будет использоваться с функцией (находится в файле sh_inif), а также со сценарием .enter.sh, который также помещен на компакт-диск (см. параграф 14.14). 838 Часть девятая. Разное
52.04 80cols * Файл SOcols содержит 80 цифр в одной строке. Этот файл можно использовать для того, чтобы проверить, равна ли ширина вашего экрана 80 символам. Проверка осуществляется посредством следующей команды: cat 80cols См. параграф 42.06. /* Команда / создает временные файлы для использования программами, которым требуются имена файлов в командной строке. Например, чтобы с помощью команды diff найти различия между двумя файлами после сортировки их содержимого, следует ввести такую команду: diff Ч sort filel' '! sort file2' См. параграф 9.18. addup * Файл addup является сценарием shell, в котором используется утилита awk для суммирования данных в определенном столбце (см. параграф 49.07). age_files * Сценарий agejiles определяет общий размер файлов заданного каталога, имеющих определенный "возраст" (см. параграф 16.25). ascii * Сценарий ascii возвращает десятичный код указанного символа (см. параграф 51.03). awf* Программа awf (Amazingly Workable Formatter — прекрасно работающая программа форматирования) представляет собой имитацию команды nroff -man или nroff -ms, написанную на языке awk (на его старой версии). Эта программа работает медленно и имеет много ограничений, однако хорошо выполняет свою работу при выводе разделов документации и простых документов с макросами пакета ms. Программу аи/" можно использовать для быстрого форматирования текста. Она хорошо подходит людям, которые любят экспериментировать. См. параграф 43.17. GNU bash версии 1.14.6 Это GNU-версия интерпретатора bash (Boume-Again Shell), интерактивного интерпретатора с синтаксисом Bourne shell. Он обладает средствами редактирования командной строки; управления заданиями на платформах, поддерживающих эту возможность; управления перечнем ранее введенных команд (как в интерпретаторе csh); раскрытия фигурных скобок, а также множеством других возможностей (см. параграф 8.02). behead * Сценарий behead удаляет из файла все строки вплоть до первой пустой строки. Это позволяет эффективно удалять заголовки из файлов, созданных посредством программ mail и news (ujj (см. параграф 35.05). bitmaps * Растровые монохромные изображения из коллекции Джеффа Посканцера предназначены для использования в качестве фоновых изображений и иллюстраций. Они хранятся в формате XII bitmap (ut). Большая часть этих изображений предоставлена в сжатом виде. Если они вам потребуются в другом формате, воспользуйтесь пакетом netpbm, который также находится на нашем компакт-диске. См. параграф 43.25. 'Что находится на компакт-диске? 839
52:04 bkedit * Сценарий bkedit предназначен для создания резервной копии файла перед его обработкой в редакторе W (см. параграф 44.1). bsdtar Утилита bsdtar очень похожа на tar, однако она может преобразовывать длинные имена файлов в уникальные 14-символьные имена, используемые в системах с ограничением длины имен файлов. Утилита только читает архивы, но не создает их. См. параграф 52.08. bsplit Утилита bsplit позволяет разделять двоичные файлы на части. Пользователи, знакомые с утилитой split, не будут иметь проблем с использованием bsplit, поскольку правила работы с ними одинаковы. См. параграф 35.09. cal_today * Это простой сценарий, запускающий программу cal и помечающий сегодняшнюю дату в выведенном на экран календаре (см. параграф 48.07). eaten Утилита calen создает календарь в 132-колоночном формате на целый год или на заданный диапазон месяцев в году (см. параграф 48.08). eatsaway * Сценарий eatsaway включен как пример использования цикла, повторяющего команду до тех пор, пока она выполняется успешно (см. параграф 44.10). center * Это flVfAr-сценарий, центрирующий строки файла (см. параграф 35.08). cgrep * Этот Perl-сценарий предназначен для эмуляции контекстной команды grep: он выводит найденную строку вместе с окружающим ее текстом (см. параграф 27.03). cgrep.sed * Этот serf-сценарий предназначен для эмуляции контекстной команды grep (выводит найденную строку вместе с окружающим ее текстом). Кроме того, он находит соответствие многострочному шаблону. См. параграфы 27.11 и 34.17. checksed * Сценарий checksed запускает команды редактора sed, содержащиеся в файле sedscr, и применяет их к заданному файлу. Результаты редактирования отображаются с помощью команды diffn программы постраничного просмотра. См. параграф 34.03. chmod_edit * Сценарий chmod_edit устанавливает право на запись в файл, запускает ваш любимый редактор для его модификации, а затем опять отменяет право на запись (см. параграф 22.09). chunksort * Сценарий chunksort сортирует многострочные записи, разделенные пустыми строками (см. параграф 36.07). cleanup * Пример сценария, который можно запустить с помощью команды сгоп. Сочетание нескольких критериев поиска для команды find позволяет запускать ее для выполнения различных операций только однажды, а не несколько раз. См. параграф 23.22. 840 Часть девятая. Разное
52.04 cteanup.sed * Это serf-сценарий, обрабатывающий /л(#-файлы. Он преобразует прямые кавычки в круглые, два дефиса в длинное тире, а также помещает непробельный символ перед фрагментом текста, набранного моноширинный шрифтом. Сценарий необходимо вызывать с помощью команды serf -/cleanup.sed. См. параграф 43.21. Clear * Сценарий Clear позволяет генерировать Escape-последовательности для терминала VT100. На него есть ссылки с именами NOG (для выключения альтернативного набора символов), Graphics (для установки графического режима), С132 (для установки 132-символьного режима), С80 (для установки 80-символьного режима), Rewid (для установки режима инверсного отображения), Normal (для восстановления обычного режима), ToStatus (для записи сообщения в строку состояния терминала) и ClrStatus (для очистки строки состояния терминала). См. параграф 41.09. els * Утилита els выводит список каталогов в красиво оформленных столбцах и отмечает длинные имена, которые она сократила. На нее есть ссылки с именами elf, cls2 и clf2. См. параграф 16.06. cols * Сценарий cols выводит данные в столбцах. На него есть ссылки с именами с2, сЗ, с4, с5, сб, с7и с8, обеспечивающие вывод в соответствующее количество столбцов. См. параграф 35.16. count.it * Утилита count.it подсчитывает разницу в словах между двумя файлами (см. параграф 29.06). count_types * Данный сценарий предназначен для подсчета количества файлов каждого типа (см. параграф 16.24). cpmod Утилита cpmod позволяет копировать права доступа, идентификатор владельца и временные характеристики из одного файла в другой, не изменяя содержимое второго файла (см. параграф 22.16). crontab * Сценарий crontab обеспечивает интерактивное редактирование записей в файле crontab. Этот сценарий предназначен для систем, которые пока не поддерживают функцию интерактивного редактирования данного файла. См. параграф 40.15. crush * Этот serf-сценарий удаляет из текста пустые строки и направляет результат в стандартный вывод (см. параграф 25.11). cshjnit * Файл csh_init содержит коллекцию команд и определений псевдонимов С shell, описанных в данной книге. Большинство из них доступны также в файле shjnit как функции интерпретаторов типа Bourne shell. В файле cshjnit каждый набор команд предварен символами комментария. Перед использованием команд эти символы необходимо удалить. Вставка символов комментария вызвана тем, что последующие определения могут отменять предыдущие и конфликтовать друг с другом. Вы можете скопировать этот файл в файл конфигурации интерпретатора shell (2.02), а затем снять комментарий у нужных определений. Можно также выполнять его посредством команды source (44.23). См. параграфы 2.15, 7.05, 7.06, 7.08, 7.11, 10.07, 10.08, 14.09, 14.14, 15.05, 16.26, 17.23, 21.14, 22.02, 22.09, 25.17, 29.08, 30.20, 43.08, 47.05 и 50.08. Что находится на компакт-диске? 841
52.04 csh_logout * Файл cshjogput содержит набор команд для удаления временных файлов. Пользователи интерпретатора С shell могут включить эти команды в свои файлы ~/.logout. См. параграф 21.03. csplit Утилита csplit делит текст на страницы согласно контексту. Эта программа входит в пакет textutils. См. параграф 35.10. cut и paste Утилиты cut и paste являются общедоступными GNU-версиями команд cut и paste, разработанных компанией AT&T. Они входят в пакет textutils. См. параграфы 35.14 и 35.18. cvtbase Утилита cvtbase предназначена для преобразования чисел из одной системы счисления в другую. Поддерживаются десятичная, шестнадцатеричная, восьмеричная и двоичная системы. См. параграф 49.05. del* Этот сценарий запрашивает у пользователя разрешение на удаление указанных файлов. В отличие от команды rm -i, сценарий выводит запрос только один раз для всех файлов, если нужно удалить более трех файлов. См. параграф 23.06. delete Утилита delete заменяет команду гт и позволяет восстановить удаленный файл. Вместо того чтобы на самом деле удалять файлы, эта программа помечает их как удаленные, добавляя к имени префикс . #. Для восстановления файла служит команда undelete. Чтобы по-настоящему удалить файлы, следует ввести команду expunge или purge. Список файлов текущего каталога, помеченных для удаления, предоставляет команда Isdel. См. параграф 23.09. GNU-версии команд diff, diff3, sdiff и стр GNU-версии команд diff, diff3, sdiff vi cmp обладают всеми возможностями соответствующих BSD-версий, а также некоторыми дополнительными возможностями (см. параграфы 28.01, 28.04 и 28.11). dir_path * Данный сценарий выводит все каталоги с одинаковыми именами (см. параграф 16.21). dirtop * Сценарий dirtop с помощью Escape-последовательностей терминала VT100 и команды Is формирует список файлов каталога. Пока вы работаете, этот список постоянно отображается в верхней части экрана. См. параграф 21.10. doublespace * Данный .serf-сценарий удваивает пробелы в тексте и направляет результат на стандартный вывод (см. параграф 25.12). ediff Утилита ediff придает выводу команды diff удобочитаемый вид (см. параграф 28.08). elookfor Сценарий elookfor подобен сценарию lookfor, но работает быстрее. Используя команду egrep, он выполняет поиск в заданном каталоге и его подкаталогах с целью обнаружения всех файлов, которые содержат определенную строку (строки). См. параграф 17.21. GNU emacs версии 19.3.1 Это GNU-реализация мощного, настраиваемого, расширяемого экранного редактора Emacs с обширной документацией (см. главу 32). 842 Часть девятая. Разное
52.04 GNU expand GNU-версия утилиты expand преобразует символы табуляции в соответствующее количество пробелов. Эта программа входит в пакет textutils. См. параграф 41.04. Expect версии 5.22 Эта программа предназначена для управления интерактивными приложениями, такими как telnet и passwd, которые предлагают пользователю что-то ввести. Для автоматизации процесса взаимодействия можно написать простые Expect-сценарии. Впоследствии такой сценарий запускает интерактивную программу в неинтерактивном режиме. Программа Expect входит в пакет Tcl/tk. См. параграф 9.26. ехтс * Файл ехгс содержит коллекцию описанных в данной книге команд редакторов v/ и ex. В этом файле каждый набор команд предварен символами комментария. Перед использованием команд эти символы необходимо удалять. Вставка символов комментария вызвана тем, что различные определения могут отменять предыдущие и конфликтовать между собой. Вы можете скопировать этот файл в свой файл .ехгс (4Щ, а затем разрешить нужные определения. См. параграфы 30.23, 30.32, 31.05, 31.09, 31.12, 31.13 и 31.16. fgrep версии 1.1 Хотя ее и ругают за медлительность, эту утилиту стоит инсталлировать, благодаря ее замечательным возможностям. Мы предлагаем вам одну из самых быстрых версий fgrep, которую нам только удалось найти. См. параграф 27.06. GNU fiteutits версии 3.13 GNU-утилиты, предназначенные для работы с файлами, имеют значительные преимущества перед своими стандартными аналогами: они обладают более высокой скоростью, дополнительными опциями и меньшим количеством ограничений. В пакет входят следующие утилиты: chmod, chgrp, chown, cp, dd, df, du, install, In, Is, mkdir, mkflfo, mknod, mv, rm, rmdir и touch. Большинство этих программ описано в данной книге. GNU find версии 4.1 GNU-версия команды find имеет более широкий круг возможностей по сравнению со стандартной командой find, применяемой в большинстве систем. В частности, она поддерживает возможность поиска файлов с указанием любого момента изменения временных характеристик, в том числе произошедшего в течение последних 24 часов (чего не может делать стандартная команда find). Кроме того, предусмотрена возможность задания пользователем глубины поиска (т.е. можно запретить поиск в подкаталогах). См. параграфы 17.11 и 17.23. Команда find распространяется также с GNU-версией утилиты xargs, которая используется для выполнения команд со многими аргументами. Ее опция -0, предназначенная для совместной работы с GNU-версией команды find, позволяет избежать проблем со стандартной командой xargs. См. параграфы 9.21 и 9.22. Наконец, в пакет входит GNU-версия программы locate, которая выбирает из базы данных список файлов, соответствующих шаблону (аналогично быстрой команде find во многих системах). См. параграф 17.18. findcmd * Эта утилита просматривает последовательность поиска и выводит все имена программных файлов, содержащие заданные символы (см. параграф 16.10). findtext * Сценарий findtext служит для обнаружения текстовых файлов (которые могут быть прочитаны людьми) среди тех, что заданы шаблоном (см. параграф 16.26). Что находится на компакт-диске? 843
52M flip * Данный Perl-сценарий меняет местами строки текста таким образом, что первая строка меняется с последней, вторая — с предпоследней и т.д. (см. параграф 25.19). GNUfmt Утилита fint упорядочивает текст по абзацам с длиной строки не более 72 символов. GNU-версия имеет несколько дополнительных возможностей форматирования. См. параграф 35.02. finish * Этот сценарий использует редактор sed и программу ито^для имитации работы команды fint. Он предназначен для систем, распространяемых без команды fint. См. параграф 35.03. formprog * Сценарий для заполнения бланков. Он ищет файл шаблона (первый аргумент), запрашивает у пользователя информацию и помещает заполненный бланк в выходной файл (второй аргумент). См. параграф 45.22. ftpfite * Сценарий для анонимной передачи файла с FTP-узла. Он помещен на компакт-диск в качестве примера использования конструкции "документ здесь". См. параграф 8.18. GNU awk (gawk) версии 3.0.3 Утилита gawk разработана организацией Free Software Foundation и является свободно распространяемым аналогом утилиты awk. Она имеет больше возможностей по сравнению с исходной версией. См. параграф 33.12. getmac * Сценарий getmac выводит макроопределения программы troff, содержащиеся в указанном пакете макросов (см. параграф 43.20). getopt Утилита getopt является свободно распространяемой версией программы getopt, имеющейся в System V (см. параграф 44.18). glimpse и agrep Пакет glimpse представляет собой индексную и запросную систему, позволяющую очень быстро выполнять поиск в гигантских массивах текста (например, во всех ваших файлах). Частью пакета glimpse является автономная утилита agrep, предназначенная для быстрого поиска текста. Утилита agrep похожа на других членов семейства grep, но имеет более общее назначение (и, как правило, является более быстрой). Одно из преимуществ перед стандартной командой grep состоит в возможности поиска по приблизительному шаблону. См. параграф 27.08. GNU gnroff версии 1.10 Это версия программы форматирования текста nroff. Входит в пакет groff. См. параграф 43.17. grabchars Утилита grabchars воспринимает одно или несколько нажатий клавиш, не требуя нажатия клавиши [RETURN]. Она разработана с целью придания сценариям большей интерактивности. См. параграф 45.32. GNU grep версии 2.0 GNU-версия команды egrep (имеющая также ссылку с именем grep) действует почти вдвое быстрее, чем стандартная команда UNIX egrep (см. параграф 27.09). GNU groff версии 1.10 Программа groff является GNU-версией программы форматирования текста troff. В пакет включены реализации программ troff, pic, eqn, tbl, refer, пакеты макросов man и ms, драйверы 844 Часть девятая. Разное
52.04 для форматов PostScript, ТЕХ и устройств типа пишущей машинки, а также модифицированная версия пакета макросов те для Berkeley-систем и расширенная версия программы xditview системы XII (из). См. параграф 43.16. GNU gzip версии 1.2.4 Утилита gzip позволяет сжимать файлы. Наряду с ней пакет содержит утилиты gunzip и gzcat (см. параграф 24.07). head * Сценарий head имитирует работу команды head, распространяемой со многими версиями UNIX. Сценарий предназначен для систем, в которых эта команда отсутствует. См. параграф 25.02. hey * Сценарий hey предназначен для тех, кто привык работать в системах, отличных от UNIX, и по привычке набирает не UNIX-команды. Сценарий выводит ядовитое замечание, указывающее на вашу ошибку, а затем запускает UNIX-команду, которую вы собирались выполнить. См. параграф 16.15. hgrep версии 0ctR88 Утилита hgrep является надстройкой программы grep. Программа hgrep берет результаты работы программы grep и выделяет найденные слова. См. параграф 27.20. index версии 1.0 Утилита index обеспечивает поддержку нескольких баз данных с текстовой информацией, при этом каждая база может иметь свой формат. Данная утилита позволяет выполнять следующие операции: добавление, удаление и редактирование записей; поиск записей с использованием полных регулярных выражений; применение фильтров ко всей базе данных или к ее части. См. параграфы 48.11 и 48.12. ipl версии 1.0 Программа Ipl представляет собой систему создания двумерной графики. Она позволяет создавать разнообразные графики (например, графики разброса), гистограммы, линейные, круговые и временные диаграммы, карты США и Канады, рамки, стрелки, надписи и т.д. Программа ipl создает выходной файл в формате PostScript, используя указанный пользователем управляющий файл. Кроме того, ipl предоставляет возможность проектировать различные таблицы и красиво оформлять их. См. параграф 49.09. ispell версии 3.1.20 Утилита ispell является экранным корректором орфографии, показывающим ошибки в тексте исходного файла, а также предлагающим возможные варианты, если таковые удается найти. По сравнению с UNIX-командой spell эта программа быстрее и намного удобнее в использовании. Наряду с английским программа ispell способна поддерживать и другие языки. См. параграф 29.02. iot Утилита jot — это простое средство получения последовательных и случайных данных. Она очень удобна при построении циклов в сценариях. См. параграф 45.11. lensort * Утилита lensort сортирует строки по возрастанию их длины (см. параграф 36.08). less версии 3.21 Это очень удобная утилита постраничного просмотра, предпочитаемая таким программам, как pg и тоге. Утилита less обладает всеми функциональными возможностями утилиты тоге, а также такими дополнительными возможностями, как обратная прокрутка, создание закладок, поиск (вперед и назад, в одном и нескольких файлах) и т.д. См. параграф 25.04. Что находится на компакт-диске? 845
52.04 If* На один и тот же сценарий ^ссылаются пять команд (If, 11, lg, lm и /г), которые вызывают команду Is с различными наборами опций командной строки (см. параграф 16.07). Indir Утилита Indir является безопасным средством копирования структуры каталога в другое место файловой системы. При смене каталога посредством команды cd с использованием символической ссылки осуществляется переход в каталог, на который указывает эта ссылка. Это может вызвать путаницу и даже оказаться опасным, если ссылка создана на критическую область файловой системы. Избежать использования каталогов-ссылок позволяет утилита Indir. Эта утилита Indir рекурсивно воссоздает структуру каталога, образуя символические ссылки на все содержащиеся в нем файлы. См. параграф 18.07. logerrs версии 1.0 * Этот сценарий направляет сообщения об ошибках в файл регистрации, а также в стандартный поток ошибок (см. параграф 13.16). longlines * Файл longlines содержит несколько строк текста длиной 200 символов. Этот файл можно использовать для проверки правильности переноса строк, а также для придания окну определенного размера с помощью команды cat longlines См. параграф 42.06. look Утилита look является быстрой и переносимой версией стандартной команды look (см. параграф 27.18). lookfor * Сценарий lookfor находит все файлы в заданном каталоге и его подкаталогах, содержащие определенную строку (строки). См. параграф 17.21. ls_today * Данный сценарий предназначен для вывода имен файлов, созданных или редактировавшихся в течение текущего дня (см. параграф 16.18). make_print * Пример файла makefile для вывода на печать файлов, которые были изменены после выполнения последней распечатки. Необходимо переименовать предлагаемый файл в makefile или Makefile и использовать для его выполнения программу make. См. параграф 21.09. manindex * Утилита manindex имитирует работу команды apropos, имеющейся во многих системах. Она генерирует предметный указатель, в котором можно вести поиск с помощью такого псевдонима: alias apropos "grep -i \!$ файл_предметного_указателя" См. параграф 50.03 motd.diff * Это сценарий, вызов которого должен производиться из файла .login. Он всего лишь отображает сообщение дня, если оно изменилось со времени последнего входа в систему. См. параграф 2.14. namesort Утилита namesort сортирует список лиц по фамилиям (см. параграф 36.09). 846 Часть девятая. Разное
52.04 netpbm версии 1таг94 Пакет netpbm является последней версией пакета pbmplus (расширенный пакет переносимой растровой графики). Он предназначен для преобразования изображений из различных форматов в переносимый формат и наоборот. Кроме преобразователей форматов пакет содержит некоторые простые средства манипулирования изображениями (см. параграф 43.25). nextday * Сценарий nextday возвращает название следующего дня недели, чтобы предоставить его команде at. Данный сценарий может иметь ссылку nextweekday. В таком случае он возвращает название следующего буднего дня (например, в пятницу команда nextweekday возвращает значение Monday — понедельник). См. параграф 40.10. по_гип * Файл nojun содержит пример сценария, который вы можете поместить в свой персональный каталог $НОМЕ/Ып с тем, чтобы впоследствии создать на него ссылки с именами программ, которые не должны выполняться в некоторых системах. Сценарий предназначен для использования в сети,.когда на нескольких машинах различной архитектуры имеются начальные каталоги с одинаковыми именами. После редактирования сценария можно создать ссылки на него с именами команд, которые в некоторых системах должны выполняться иначе (или вообще не выполняться). Естественно, каталог (например, $НОМЕ/Ып), содержащий инсталлированный сценарий, на который направлены ссылки, должен быть указан в последовательности поиска перед любыми системными исполняемыми файлами. См. параграф 8.08. пот * Утилита пот (по match — несовпадение) выдает имена файлов текущего каталога, которые не соответствуют указанному шаблону. Например, чтобы получить для редактирования все файлы текущего каталога, которые не заканчиваются на .о, введите такую команду. vi "nom *.о" См. параграф 15.09. offset * Сценарий offset служит для создания отступов в тексте, предназначенном для печати или других целей (см. параграф 35.07). oldtinks * Данный сценарий выводит имена "висящих" символических ссылок (см. параграф 16.28). opttest * Сценарий для анализа вывода команды getopt. Он предназначен для демонстрации работы этой команды. См. параграф 44.18. paircheck * Пример сценария для проверки того, имеют ли определенные строки файла соответствующие парные строки. Сценарий проверяет, есть ли в данном troff-файле соответствующий макрос .ТЕ для макроса .TS, однако его легко изменить для проверки других пар строк. См. параграф 29.09. patch версии 2.1 Эта утилита написана Ларри Уоллом и предназначена для распространения дополнений ("заплат") к исходным файлам. Используя результаты проверки файлов, полученные с помощью команды diff (как правило, применяются контекстные команды diff), программа patch вносит изменения в файл. Профамма patch широко используется для пересылки изменений исходных файлов во всем мире. См. параграф 33.09. Что находится на компакт-диске? 847
52.04 peal версии 4.3 Утилита peal генерирует PostScript-код для создания календарей горизонтальной или вертикальной ориентации на любой месяц и год. По умолчанию утилита peal выводит пустой календарь. Ее ценность состоит в возможности помещения "события" в ячейку определенного дня, что позволяет пользователю создавать персонифицированные календари. См. параграф 48.09. perl версии 5 Perl (Practical Extraction and Report Language — практический язык для создания отчетов и выборок) является языком интерпретирующего типа, оптимизированным для обработки любых текстовых файлов, извлечения из них информации и построения отчетов на базе пелученной информации. Интерпретатор perl располагает мощными подпрограммами для быстрого создания утилит с обширными возможностями по обработке строк, работе с файлами и использованию системных ресурсов. Встроенный отладчик делает программу perl привлекательной альтернативой утилитам awk и gawk. Программа perl версии 5 обновлена и улучшена по сравнению с версией 4. Некоторые нововведения описаны в параграфе 37.05. phone * Этот сценарий записывает в стандартный вывод строки файла phone, соответствующие заданному шаблону. На данный сценарий имеется ссылка с именем address, при активизации которой выводятся соответствующие строки из файла address. См. параграф 48.02. plpegrep * Утилита pipegrep производит поиск в выходных данных последовательности команд, отображая имя команды, которая сформировала ту или иную искомую строку (см. параграф 27.13). pstext Эта утилита служит для преобразования текстовых файлов в формат PostScript (обычно для PostScript-принтера). Она включает опции для печати в "альбомном" (landscape) режиме, а также для задания шрифта и указания его размера. См. параграф 43.22. psutils версии 1.17 Коллекция утилит для манипулирования PostScript-документами — перекомпоновки страниц, выборочной печати страниц и т.п. (см. параграфы 43.23 и 43.24). pushin * Данный .serf-сценарий удаляет из файла лишние пробельные символы и направляет результат в стандартный поток вывода (см. параграф 25.13) qesh * Файл qesh содержит функцию, которая позволяет использовать в интерпретаторах Bourne shell и Кот shell такую возможность интерпретатора С shell, как оператор {} (9.09. См. параграф 15.03. qsubst Утилита qsubst предназначена для замены строк в файлах большого объема. Она принимает список имен файлов и две строки. В каждом указанном файле утилита qsubst вместо строки! подставляет строку2, если пользователь подтверждает изменение. См. параграф 33.10. qterm Утилита qterm "опрашивает" терминал с целью выяснения его типа. Она применяется для автоматического определения типа терминала. Утилита выводит название терминала (например, vtlOO). Это название, скорее всего, соответствует имени, содержащемуся в базах данных termcap и terminfo вашей системы. См. параграф 5.05. res версии 5.7.0 Пакет res (Revision Control System — система управления версиями) представляет собой набор команд для работы с многочисленными версиями файлов. Пакет автоматизирует хранение, извлечение, регистрацию, идентификацию и объединение версий. Его удобно применять при 848 Часть девятая. Разное
52.04 работе с текстовыми файлами, которые часто подвергаются редактированию (например, с программами, документацией^ рисунками, статьями и письмами). См. параграф 20.14. rcsegrep.fast * Сценарий rcsegrep.fast (большая часть которого написана на языке утилиты nawk) ищет заданную строку в последних версиях одного или нескольких RCS-файлов. Отличительной чертой этого сценария является быстродействие, поскольку он напрямую читает RCS-файл. Его рекомендуется применять только при наличии большого количества просматриваемых файлов; в противном случае лучше воспользоваться сценарием rcsgrep. См. параграф 27.10. rcsgrep * Этот сценарий ищет заданную строку в версиях RCS-файлов. Его можно вызывать посредством ссылок rcsegrep и rcsfgrep. См. параграф 27.10. rcsrevs * Сценарий rcsrevs выводит номера всех версий, хранящихся в RCS-файле (см. параграф 20.15). recomment * Данный сценарий запускает программу fint (зо.з7) для форматирования строк с комментариями в текстовых файлах (см. параграф 35.04). redo * Утилита redo позволяет просматривать, редактировать и выполнять команды из перечня ранее введенных команд интерпретатора С shell. Это сценарий для С shell, который выполняется посредством команды source и псевдонима, определенного в файле cshjnit. См. параграф 11.14. relink * Perl-сценарий для обновления ссылок на несколько файлов, аналог сценария rename (см. параграф 18.14). геп Утилита геп позволяет переименовать несколько файлов согласно шаблонам поиска и замены, как это делается в системе VMS (но лучше). Данная утилита следит за конфликтами при переименовании и аккуратно выполняет цепочку переименований. См. параграф 18.11. rename * Perl-сценарий для переименования нескольких файлов (см. параграф 18.10). rot Утилита rot "поворачивает" содержимое файла таким образом, что строки превращаются в колонки, а колонки — в строки. При отсутствии опций содержимое файла будет "повернуто" по часовой стрелке. См. параграф 35.23. runsed * Для обработки указанных файлов сценарий runsed запускает команды редактора sed, содержащиеся в файле sedscr, и записывает исправленные версии файлов поверх исходных (см. параграф 34.03). runtime * Сценарий runtime несколько раз выполняет программу time с заданной командой, а затем выводит среднее время выполнения команды по всем итерациям. При этом вывод команды игнорируется. См. параграф 39.04. sc версии 6.21 Утилита sc производит вычисления в прямоугольных электронных таблицах (см. параграф 49.08). — " '">' 'НИМ," ■ - .. . .*£. ■ ■ Что находится на компакт-диске? 849
52.04 screen версии 3.7.2 Утилита для работы с окнами. Позволяет организовать несколько независимых экранов (псевдотерминалов (pty) (4i.M)) на одном физическом терминале. См. параграфы 12.09 и 3.07. screensize * Файл screensize содержит 69 строк текста с цифрами от 69 до 1. Вы можете воспользоваться им, определяя количество отображаемых на экране строк с помощью следующей команды: cat screensize См. параграф 42.06. script.tidy * Сценарий script.tidy применяется для удаления файлов, созданных программой script (см. параграф 51.06). seareh.el * Файл seareh.el содержит набор команд редактора Emacs, предназначенных для поиска. Чтобы воспользоваться этими командами, необходимо из файла $HOME/.emacs выполнить команду load-file, указав ей файл seareh.el. См. параграф 32.08. sedman * Это serf-сценарий для форматирования простых дави-страниц (см. параграф 43.19). shjnit * Файл shjnit содержит набор описанных в книге функций и псевдонимов интерпретаторов типа Bourne shell. Большинство из них приведены в виде функций в файле csh_init. В этом файле каждому набору команд предшествуют символы комментария. Перед активизацией команд необходимо удалять эти символы. Вставка символов комментария обусловлена тем, что различные определения могут отменять предыдущие и конфликтовать между собой. Вы можете скопировать этот файл в файл конфигурации интерпретатора shell (2.02), а затем разрешить нужные определения. Для его выполнения можно также воспользоваться командой source (44.23). См. параграфы 2.15, 7.05, 7.06, 7.08, 7.11, 7.12, 14.09, 14.14, 15.05, 16.26, 16.27, 17.04, 17.23, 22.02, 22.09, 25.17, 29.08, 30.20, 43.08 и 50.08. shjogout * Файл shjogput содержит набор команд для удаления временных файлов. Пользователи интерпретаторов типа Bourne shell могут включить эти команды в свои файлы, выполняемые при выходе из системы (например, .bashjlogpui). См. параграф 21.03. 6NU sharutils версии 4.2 Пакет GNU-утилит sharutils содержит набор инструментальных средств для создания и распаковки shell-архивов. Этот пакет облегчает распространение исходных файлов по электронной почте. Он включает также программы shar, unshar, uuencode, mdecode, mail-flies, matlshar и remsync. 6NU shellutils версии 1.11 В состав данного пакета входят небольшие утилиты, применяемые при программировании. Эти утилиты, как правило, более надежны и имеют больше возможностей, чем их системные аналоги. Пакет содержит следующие утилиты: basename, date, dtrname, env, expr, false, groups, id, nice, nohup, pathchk, printenv, printf, sleep, stty, tee, test, true, tty, uname, who, whoami и yes. Большинство утилит описано в данной книге. Инсталлируя утилиты nice, stty и uname, помните, что для их работы требуются средства, которые имеются не во всех системах. showmatch * Этот сценарий демонстрирует содержащиеся в заданном файле строки, которые соответствуют определенному регулярному выражению (см. параграф 26.06). 850 Часть девятая. Разное
S2.04 si* Данный Perl-сценарий показывает настоящее имя файла, соответствующее символической ссылке (см. параграф 18.08). sis Утилита sis разработана с целью преодоления ограничений, присущих стандартной UNIX-ko- манде Is. Она обеспечивает вывод более полной информации об индексном дескрипторе файла. Эта утилита особенно удобна в сценариях, где она облегчает получение информации о файлах. Для управления сортировкой и выводом информации о файлах применяются строки форматирования, как в команде printf. См. параграф 16.29. smiley Утилита smiley — это "сервер" значков эмоций. Она предоставит вам объяснение любого известного ей значка эмоций или выведет значок, выбранный случайным образом. См. параграф 51.12. squoze * Сценарий для сжатия каталогов очень большого объема (см. параграф 24.16). stat Утилита stat выводит содержимое индексного дескриптора файла (полученное от системного вызова stat{2)) в удобном для просмотра формате (см. параграф 21.13). stree * Данный сценарий демонстрирует структуру дерева каталогов (см. параграф 16.19). stripper * Это простой сценарий для удаления отладочной информации из двоичных файлов, содержащихся в каталоге $НОМЕ/Ып (см. параграф 24.13). SU* Сценарий su является надстройкой к тем версиям системной команды su, в которых неправильно устанавливается начальный каталог или имя пользователя при запуске нового интерпретатора shell (см. параграф 22.22). GNU tar версии 1.11.2 GNU-версия утилиты tar совместима со стандартной утилитой tar, поставляемой с операционной системой. Она имеет много новых возможностей, в том числе такие, как работа с удаленными устройствами, сжатие, многотомные архивы, извлечение из архива с записью в стандартный поток вывода, извлечение файлов, соответствующих шаблону, интерактивное подтверждение, извлечение только недостающих файлов, а также сохранение только новых файлов, имеющих дату не ранее заданной. См. параграф 19.06. кар Утилита tcap напоминает программу (put и предоставляет сценариям доступ к Escape-последовательностям, содержащимся в базе данных termcap. Эта утилита в основном распространяется под именем tc. Мы ее переименовали во избежание путаницы со стандартной командой tc, распространяемой со многими операционными системами. См. параграф 41.10. Tel и tk версий 4.2р2 и 7.6р2 Tel — это широко используемый язык интерпретирующего типа. Он помещен на компакт-диск главным образом из-за профаммы Expect, которая применяет его. Однако язык Tel очень ценен и сам по себе. Язык сценариев tk позволяет интегрировать в приложения фафический пользовательский интерфейс. См. параграф 926. Что находится на компакт-диске? 851
52M tcsh Версия интерпретатора С shell, в которую добавлены редактор командной строки, возможность дополнения командной строки и имен файлов, возможность вывода списка файлов и т.д. (см. параграф 8.03). termtest * Сценарий termtest быстро выводит на экран повторяющиеся символы. Его можно применять для проверки связи с терминалом, отслеживания шумов в линии связи и пропавших символов. См. параграф 42.07. GNU textutils версии 1.20 Этот пакет GNU-утилит содержит утилиты для просмотра, манипулирования, отображения и комбинирования текстовых файлов. В пакет входят утилиты csplit, cut, paste, expand и unexpand. tgrep Сценарий tgrep производит поиск только в тех файлах, которые содержат текст. Он удобен для просмотра каталогов, включающих как двоичные, так и текстовые файлы. См. параграф 27.13. fro версии 1.1 * Сценарий tm выводит значение текущего времени в любой стране мира (см. параграф 6.07). tpipe Простая утилита, разделяющая конвейер на две части. Как и программа tee, утилита tpipe передает данные со своего стандартного ввода в стандартный вывод. Однако в отличие от программы tee, записывающей копию стандартного ввода в файл, утилита tpipe передает копию стандартного ввода на вход другого конвейера, который указан в качестве аргумента команды tpipe. См. параграф 13.11. tputinit Сценарий tputinit имитирует команды инициализации терминала, генерируемые командой tput init. Его можно использовать в системах с более старой версией команды tput, которая не поддерживает ключевое слово init. См. параграф 5.12. triplespace * Этот .serf-сценарий утраивает пробелы в тексте и направляет результат на стандартный вывод (см. параграф 25.12). twin Утилита twin предназначена для сравнения двух похожих файлов. Они отображаются рядом, причем несовпадающие строки выделяются инверсно. См. параграф 28.05. 6NU unexpand версии 1.20 GNU-версия утилиты unexpand преобразует символы пробела в символы табуляции, соответствующие 8 пробелам. Утилита unexpand входит в пакет textutUs. См. параграф 24.06. vgrep * Сценарий vgrep выводит список имен файлов, не содержащих заданной строки (см. параграф 15.08). vis Утилита vis периодически выполняет заданную команду и обновляет ее вывод на экране (см. параграф 51.07). 852 Часть девятая. Разное
52.04 vtree версии 1.2 Утилита vtree выводит дерево каталогов для указанной части файловой системы. Благодаря опциям этой утилиты вы сможете определить объем дискового пространства, занимаемого каждым каталогом, количество индексных дескрипторов и т.д. См. параграф 16.20. watchq * Этот сценарий-демон о.М) следит за очередями к нескольким принтерам и посылает пользователям сообщения при возникновении ошибок (см. параграф 38.11). whereiz * Утилита whereiz выводит список всех исполняемых файлов в вашей последовательности поиска, которые соответствуют заданному имени (см. параграф 4.10). which Утилита which на компакт-диске — это улучшенная версия стандартной утилиты which. Она возвращает полностью раскрытый аргумент командной строки, будь то псевдоним, функция или путь к исполняемому файлу. См. параграф 50.08. wordfreq * Утилита wordfreq сообщает, сколько раз каждое слово встречается в данном файле (см. параграф 29.07). GNU xargs версии 4.1 GNU-версия утилиты xargs применяется для выполнения команды со многими аргументами. Ее опция -0 предназначена для работы с GNU-версией команды find и позволяет избежать проблем со стандартной командой xargs. Эта программа входит в пакет GNU find. См. параграфы 9.21 и 9.22. xgrep Большинство утилит, подобных grep, ищут строки, которые соответствуют шаблону, а затем выводят целую строку. Программа xgrep является serf-сценарием, который извлекает только совпадающий текст, а не всю строку. См. параграф 43.21. xtail Утилита xtail следит за увеличением размера файла. Она подобна команде tail -f, но может использоваться для одновременной работы со многими файлами. См. параграф 25.18. zap * Утилита zap позволяет уничтожать процессы в интерактивном режиме путем запуска команды ps и последующего вывода запроса относительно уничтожения каждого процесса, о котором сообщила эта команда. Утилита поставляется вместе со сценарием pick, который предоставляет возможность манипулировать аргументами командной строки. См. параграф 38.13. zloop * Сценарий для запуска команд обработки набора сжатых файлов (см. параграф 24.10). zmore * Сценарий zmore запускает программу тоге для сжатых файлов. На этот файл создаются ссылки с именами zpg и zless. Наряду с указанными существуют ссылки vmore, vpgn vless, с помощью которых отображаются непечатаемые символы. Наконец, ссылки rcsmore, rcspg и rcsless постранично выводят последние версии файлов, содержащихся в RCS-файле, без создания промежуточного файла. См. параграф 25.05. zvi* Данный сценарий служит для запуска редактора vi со сжатыми файлами. Он имеет ссылки Zfix и zed для запуска со сжатыми файлами редакторов ех и ed. См. параграф 24.11. - Ш, JP Что находится на компакт-диске? 853
S2.05 52.05 Работа с прилагаемым компакт-диском [Помимо данного параграфа обязательно прочтите файл README, в котором приведены свежие рекомендации по использованию компакт-диска. — LM] Прилагаемый компакт-диск можно использовать двумя способами. Первый способ — смонтировать его только на время копирования программного обеспечения на локальный жесткий диск. В этом случае компакт-диск является носителем для распространения программ, подобным магнитной ленте или дискете. Второй способ — смонтировать компакт-диск таким образом, чтобы он постоянно присутствовал в системе как локальный жесткий диск, предназначенный только для чтения. В данном случае компакт-диск представляет собой отдельную файловую систему, для просмотра которой подходят обычные команды UNIX. Форматы компакт-дисков Компакт-диск, поставляемый в комплекте с книгой, соответствует стандарту ISO 9660. Иногда этот стандарт называют "High Siena", однако между двумя указанными форматами есть различия. 9660 является тем стандартом, который в будущем будут поддерживать большинство дисководов компакт-дисков. Для пользователей UNIX стандарт ISO 9660 может оказаться неожиданностью. Например, если компакт-диск соответствует указанному стандарту, то структура каталога на этом диске может иметь следующий вид: % is -f /cdrom ALPHA/ CONFIG1 HP700/ INSTINFO/ RS6000/ SUN4C/ BUILD.PT* CONFIG2.REV 1386/ LINUX/ SOURCES/ COMMON/ CONFIG3.Z INSTALL.PT* README SSOL2/ Согласно стандарту ISO 9660, имя файла содержит символы одного регистра и включает не более восьми символов в основной части и трех символов в расширении. Если в имени файла отсутствует точка, она добавляется в конце имени. В конце имени файла после точки с запятой может быть добавлен номер версии (номера версий употребляются в некоторых файловых системах, например в VMS). В некоторых системах эти возможности ие используются, в связи с чем возникают варианты имен. Например, файл install.pt может фигурировать под любым из приведенных ниже имей, в зависимости от того, в какой системе смонтирован компакт-диск: INSTALL.PT;1 INSTALL.PT install.pt;l install.pt Имена каталогов содержат до 8 символов одного регистра. Каталог с именем SOURCES может быть представлен как SOURCES или sources. Стандарт ISO 9660 ограничивает глубину вложения каталогов до 8 уровней. Заметим, что, задавая имена файлов в интерпретаторах shell UNIX-систем, точку с запятой перед номерами версий необходимо защищать с помощью обратной косой черты (8.Ы). В противном случае вы получите такое сообщение: INSTALL.PT: Command not found. 1: Command not found. К счастью, предоставляемые нами программы инсталляции избавят вас от необходимости придерживаться этих ограничений. Монтирование компакт-диска Для пользователей UNIX компакт-диск должен быть доступен в качестве файловой системы. В большинстве случаев можно воспользоваться стандартной командой mount. Это выглядит так: % mount имя_двсковода катллор_ыоятжроваяжя Имя дисковода зависит от типа системы. Если имя устройства вам не известно, просмотрите поставляемую с системой документацию. Номер устройства для SCSI-дисковода может быть разным, что зависит от системы. Этот номер является частью имени устройства, например /dev/rz3c/ — это имя дисковода со SCSI-номером З на станции DEC. 854 Часть девятая. Разное
52.05 Каталог_монтирования — это каталог, который будет родительским для компакт-диска после его монтирования. В большинстве систем непривилегированным пользователям не предоставляется право монтировать компакт-диск. В некоторых случаях правом на монтирование компакт-диска и работу с ним обладает только суперпользователь (1.24). По этой причине значительная часть данного парафафа посвящена вопросам системного администрирования и командам для суперпользователя. Может оказаться, что инсталлировать профаммное обеспечение с нашего компакт-диска придется системному администратору. Наш компакт-диск предназначен только для чтения. Об этом необходимо сообщить профамме mount, иначе эта профамма попытается получить доступ к дисководу для записи и выдаст сообщение об ошибке. Некоторым системам нужно указывать также тип монтируемой файловой системы, если она не является стандартной (uf з или nf з). Профамма mount может иметь опции, которые определяют, какие возможности, предусмотренные стандартом ISO 9660, будут предоставляться (например, номера версий). Так, в системе SunOS 4.1.1 компакт-диск может быть смонтирован посредством команды # /etc/mount -r -t hsfs /dev/srO /cdrom Данная команда монтирует компакт-диск (Jdev/srO) в каталоге /cdrom в режиме чтения (-г). Если опустить опцию -г, команда mount выведет такое сообщение об ошибке: mount_hsfs: must be mounted readonly mount: giving up on: /cdrom Опустив тип файловой системы hsf з (High Sierra Filesystem — предшественница ISO 9660), получим следующее: mount: /dev/srO on /cdrom: Invalid argument mount: giving up on: /cdrom Для каждой операционной системы характерна своя процедура монтирования компакт-диска. Необходимо просмотреть раздел интерактивной документации, посвященный команде mount, и найти там информацию о CD-ROM, ISO 9660 или High Sierra: % man mount Приводим варианты команды mount для поддерживаемых систем: Sun4 SunOS 4.1.1: # /etc/mount -r -t hsfs /dev/srO /cdrom IBM RS/6000 AIX 3/2: # /etc/mount -r -v cdrfs /dev/cdO /cdrom HP 700 HP-UX: # /etc/mount -r -s cdfs /dev/dsfc/c201d2s0 /cdrom SCO UNIX: # /etc/mount -r -fHS,lower,intr,soft,nov«rs /dev/cdO /cdrom Solaris: # /«to/mount -F -hsfs -r /dev/dsfc/c0t4d0s0 /cdrom Alpha: # /usr/sbin/mount -t cdfs -o noversion /dev/rz3c /cdrom Linux: # /bin/mount -t iso9660 -o ro /dev/cdrom /cdrom Что находится на компакт-диске? 855
52.05 А это пример команды mount для рабочих станций Silicon Graphics: SGI IRIX 4.x: # /usr/atc/mount -o ro,notranslate -t iso9660 \ /dav/scsi/scOd510 /cdrnm Можно также запустить процесс cdromd. # cdromd -о го,notranslate -d /dev/scai/sc0d510 /cdroo Чтобы смонтировать компакт-диск, просто вставьте его в дисковод. Демонтировать его позволит команда eject. После того как компакт-диск смонтирован, запустите программу инсталляции, которая скопирует предварительно скомпилированные двоичные файлы на жесткий диск. Программа инсталляции разработана компанией Ready-to-Run Software, которая дала ей имя Smart Installation System. Инсталляция скомпилированных двоичных файлов Компакт-диск содержит две утилиты инсталляции. Первая инсталлирует предварительно скомпилированные двоичные файлы. Вторая строит программы из исходных текстов. Если вы работаете на одной из поддерживаемых платформ, то можете инсталлировать программы с компакт-диска и использовать их немедленно. Если же ваша платформа не относится к поддерживаемым или вы хотите каким-то образом изменить программы, скомпилируйте их ИЗ ИСХОДНЫХ текстов (52.08). На компакт-диске находятся двоичные файлы для следующих платформ: • Red Hat Linux 4.1; • Sun4 Solaris 2.5; • Sun4 SunOS 4.1.4; • Digital Equipment Corp. 3.2; • IBM RS/6000 AIX 3.2.5; • HP 700 HP-UX 9.01; • SCO UNIX 3.2.4. Предварительно скомпилированные двоичные файлы можно применять в несколько более старых или более новых версиях операционных систем, чем те, что перечислены выше. Ряд программ использует возможности, не поддерживаемые всеми платформами. Утилита инсталляции выводит предупреждение, если версия программы для вашей платформы отсутствует. Если вы не знаете точного названия версии своей операционной системы, вам на помощь придет команда uname: % uname -a SunOS ruby 4.1.1 1 sun4m При отсутствии вашей платформы в приведенном выше списке попытайтесь построить программы из исходных текстов, как описано в параграфе 52.08. Структура каталога При инсталляции любого пакета с компакт-диска все файлы размешаются в каталогах, которые имеют одну и ту же структуру. На основании этой структуры (а также ее небольших вариаций) можно определить, где будет инсталлирован тот или иной пакет в вашей системе. Типовая структура каталога, в который заносятся файлы пакета, показана на рис. 52.1. У некоторых пакетов иная структура каталогов: они имеют дополнительные каталоги на уровне, обозначенном на рисунке как <каталог_инсталляими>. 856 Часть девятая. Разное
52.05 1 Ып Локальнь } I j . <тталог ь lib е i man И i J инсталляцию J 1 hardcopy | doc | Общие i sbin textnfb ,„„,„, 1 slib slrtclude sspool ! ! i Рис. 52.1. Типовая структура каталога Общие файлы — это платформно-независимые файлы, которые совместно используются многими компьютерами в сети посредством сетевой файловой системы (NFS) (из). Таковыми, как правило, являются текстовые файлы, например разделы интерактивной документации и файлы заголовков. При совместном доступе к файлам уменьшается необходимый объем дискового пространства, поскольку несколько различных систем используют один и тот же набор файлов, который не копируется на каждую машину. Кроме того, упрощается администрирование файлов, так как необходимо поддерживать только одну их копию. Локальные файлы специфичны для данной машины и архитектуры и, как правило, не могут использоваться совместно (за исключением других серверов той же архитектуры). Обычно это двоичные файлы или файлы данных, зависящие от конкретной архитектуры. Утилита инсталляции предоставляет возможность распределять общие и локальные файлы по разным каталогам. Используя эту схему, можно поместить общие файлы в раздел или каталог, доступный на нескольких машинах. При необходимости сохранить общие и локальные файлы на одном и том же уровне каталогов имена общих каталогов должны начинаться с з. Например, каталог sbin является общим, а bin — локальным.* На рис. 52.2 показано, как организованы стандартные инсталлируемые каталога. /uer/loca! share bin lib Локальные man hardcopy doc bin texlnfb lib include spool Общие Рис. 522. Станцартная структура инсталлируемых каталогов Можно также удалить префикс s из имен общих каталогов sSnclude, sspool, sbin и slib. Таким образом, каталог slib будет присоединен к каталогу lib, a sbin — г; Ып, если общие и локальные файлы хранятся в одном и том же каталоге. Что находится на компакт-диске? 857
52.05 Эту структуру можно изменить с помощью утилиты инсталляции, создав при этом все необходимые каталоги (если у вас есть право делать это). Чтобы запустить программу после ее инсталляции, необходимо указать в последовательности поиска путь <каталог_инсталляцш>/Ып (или <каталог_инстаяляции>^Наге/Ып, если выбрана стандартная структура каталогов). Например, если в качестве <каталога_инсталляции> задан каталог /usr/local, сформируйте последовательность поиска (8.07) в соответствии с правилами, действующими для вашего интерпретатора shell: % sat paths($path /usr/local/bin /usr/local/share/bin) csh $ PATH=$PATH:/usr/local/bin:/usr/local/share/bin ; export PATH sh Утилита инсталляции автоматически выводит рекомендуемую последовательность поиска по окончании инсталляции. Запуск процесса иистадляции Итак, начинаем инсталляцию. Прежде всего вы должны приобрести статус суперпользователя и смонтировать компакт-диск. Например, на компьютере Sun с операционной системой SunOS 4.1.4 необходимо выполнить следующие команды: % su Password: # /ate/mount -г -t hsfs /dev/srO /cdxom Затем перейдите в каталог, который выбран для монтирования компакт-диска, и изучите его содержимое. Мы выбрали каталог /cdrom: # cd /cdram # Is common install.pt alpha linux hp700 instinfo rs6000 sun4c 1386 readme sources ssol2 Этот вывод может выглядеть иначе, что зависит от типа системы: ALPHA/ CONFIG1 НР700/ INSTINFO/ RS6000/ SUN4C/ BUILD.PT* CONFIG2.REV 1386/ LINUX/ SOURCES/ COMMON/ CONFIG3.Z INSTALL.PT* README SSOL2/ Одним из первых действий утилиты инсталляции является копирование программ в область временного хранения (по умолчанию — в каталог /imp). Вы можете выбрать другой каталог, установив соответствующее значение для переменной среды TMPDIR. Например, чтобы использовать в качестве области временного хранения каталог /mondo, выполните такие команды: % satanv TMPDIR /mondo csh $ TMPDIR=/mondo; export TMPDIR sft Примечание: Если вы хотите инсталлировать программу в каталогах с системными файлами, то, скорее всего, столкнетесь с проблемой прав доступа к файлам. В этом случае вы можете стать суперпользователем и забыть о подобных проблемах, но это чревато перезаписью ранее установленных программ. Альтернативным решением является запуск процесса инсталляции непривилегированным пользователем, что предотвращает повреждение системных файлов. Однако при этом придется воспользоваться командой chmod (22.07), чтобы сделать каталоги доступными для записи, либо групповыми правами доступа (22.п). Как работает утилита инсталляции Если компакт-диск смонтирован правильно, можно активизировать команду инсталляции, действующую в вашей системе. Например: % ./inBtaU.pt 858 Часть девятая. Разное
52.05 или % ./INSTALL.PT\;1 Утилита инсталляции выведет меню и попросит вас ответить на вопросы. Она также попытается определить тип машины, на которой работает, и каталог монтирования компакт-диска: Assuming CDROM is mounted at /archive/cdrom Assuming MACHINE is ALPHA Welcome to Ready-to-Run Software's * Smart Installation System * This installation system requires write permission in /tmp (or $TMPDIR if it's set) directory (for staging the install) and write permission in the installation directory for the actual install (these may be the same). В этом месте утилита инсталляции может спросить, не хотите ли вы использовать в процессе инсталляции другое значение umask (22.04/. Use umask of 022 instead of 007 for install [у]? у Это повлияет на права доступа к программам после их инсталляции. Предлагаемое значение — 022 — позволяет каждому выполнять или читать программы, однако право модификации остается за вами. Далее утилита инсталляции выводит список имеющихся программ: The PowerTools2 package contains the following scripts: ! 80cols Clear _emacs_ml _enter_csh _enter_sh _exit_csh _exit_sh addup age_files ascii awf behead bkedit cal_today catsaway center cgrep The PowerTools2 package contains the following packages: bash bitmaps bsdtar bsplit calen cpmod cvtbase delete diff ediff emacs fgrep fileutils find gawk getopt glimpse grabchars В списке пакетов программ вы должны указать, какие из них устанавливать: Enter the name of package to install or choose Search/ Quit or All <package>, S (earch), A (11), Q(uit) [Search]? Теперь можно ввести название пакета, задать поиск нужного пакета, выйти из утилиты инсталляции или установить все пакеты за один прием. Инсталляция одной программы Не исключено, что вы захотите инсталлировать программы выборочно. Предположим, вы ищете программы, работающие с терминалами, но не знаете, как эти программы называются. Для поиска можно воспользоваться командой Search главного меню. Получив запрос, нажмите [RETURN], поскольку взятая в квадратные скобки команда Search является действием, выполняемым по умолчанию. <package>, S (earch), А (11), Q(uit) [Search]? s Далее введите ключевое слово поиска — terminal: Search package descriptions for (? for help) []? terminal В результате будут выведены названия всех подходящих пакетов: The descriptions for the following packages mention "terminal": 1. Clear Что находится на компакт-диске? 859
52.05 2. 3. 4. 5. 6. 7. A. N. emacs qterm screen tcap termtest tputinit ALL NONE Choose one please: В данном примере выбрана программа qterm (пункт 3): Choose one please: 3 Теперь выводится описание программы: qterm - version 5.0 Version 5.0 Qterm is a program that queries terminals to find out what kind of terminal is responding. It is useful to automatically define your terminal type. It prints the name of the terminal (compatible, hopefully, with a termcap/terminfo name) such as "vtlOQ" to standard output. The•qterm package is approximately 64Kb 32Kb - Required 15Kb - Shared: Formatted Man pages 3Kb - Shared: Other Shareable files 13Kb - Shared: Unformatted Man pages Install qterm fy]? qterm was compiled and made "Ready-to-Run" by Ready-to-Run Software, Inc. Copyright (c) 1990 Michael A. Cooper This software may be freely distributed provided-it is not sold for profit and the author is credited appropriately. Power Tools distribution by permission of the author. ************************ После вывода информации о программе qterm появляется сообщение о том, сколько места она занимает, а затем задается вопрос, хотите ли вы ее инсталлировать. Нажмите [RETURN], чтобы инсталлировать программу, или [п] и [RETURN], чтобы отказаться от этого. Мы хотим инсталлировать программу qterm, и поэтому нажмем [RETURN]. The qterm package is approximately 64Kb 32Kb - Required 15Kb - Shared: Formatted Man pages 3Kb - Shared: Other Shareable files 13Kb - Shared: Unformatted Man pages Install qterm Lyl ? 1RETURMI Далее вам предоставляется возможность ознакомиться с информацией об авторских правах на пакет qterm: qterm was compiled and made "Ready-to-Run" by Ready-to-Run Software, Inc. 860 Часть девятая. Разное
52.05 Copyright (с) 1990 Michael A. Cooper This software may be freely distributed provided it is not sold for profit and the author is credited appropriately. ************************ После этого выводятся вопросы относительно инсталляции и стандартные ответы в квадратных скобках. Где инсталлировать программное обеспечение? По умолчанию программа инсталляции предполагает, что каталогом инсталляции является /usr/local. Чтобы использовать другой каталог, введите его имя по запросу. Тогда в дальнейшем указанное имя каталога будет использовано при выводе запросов. Install package at dir [/usr/local]? /work/tools IRETURNl Где инсталлировать общие файлы пакета?По умолчанию в этих целях применяется подкаталог share каталога, указанного при ответе на предыдущий вопрос. Install shared files at [/work/tools/share] ? IRETCJRWI Удалять префикс s в именах каталогов, где инсталлированы общие файлы? По умолчанию дается положительный ответ. Convert slib->lib, sbin->bin, sspool->spool, sinclude->include [у]? (RETURN] Сколько общих файлов инсталлировать — все, некоторые или ни одного? По умолчанию устанавливаются только некоторые отобранные общие файлы. Install Shared filea (All, Some, None) [s]? IrEtURN] Инсталлировать интерактивную документацию? По умолчанию производится копирование всей документации. Можно не устанавливать неформатированные разделы при нехватке дискового пространства или отсутствии программы «го#для форматирования таких разделов. Install Unformatted Мал pages (Approx 13КЬ) [у] ? IRETURN] Install Formatted Man pages (Approx 15Kb) [y]? IRETURNl Последний вопрос касается остальных общих файлов. Install Other Shareable files (Approx ЗКЬ) [у] ? |RETURN| (Другие пакеты могут содержать другие вопросы. Например, при инсталляции утилиты gawk задается вопрос, устанавливать ли файлы Техшга и документацию в форматах PostScript и DVI.) Чтобы некоторый ответ учитывался во всех последующих пакетах без вывода дополнительных вопросов, введите его прописными буквами. Если набрать имя каталога прописными буквами, сценарий инсталляции преобразует их в строчные. Например: Install package at dir. [/usr/local]? /WORK/TOOLS Ireturn] Install ahared.files at [/work/tools/share]? /WORK/TOOLS/SHARE IRETURNl Convert slib->lib, sbin->bin, sspool->spool, sinclude->include [y]? Y IRETURNl Install Shared files (All, Some, None) [s]? S IRETURNl Install Unformatted Man pages (Approx 13Kb) [y]? Y IRETURNl Install Formatted Man pages (Approx 15Kb) [y]? Y IRETURNl install Other Shareable files (Approx 3Kb) [y]? Y IRETURNl После получения ответов на все вопросы сценарий инсталляции устанавливает необходимые параметры и еще раз спрашивает, являются ли они правильными. Если ваше отношение к выбранной конфигурации изменилось, наберите п, и вам будет предоставлена возможность переустановить некоторые инсталляционные параметры или вообще прервать инсталляцию пакета. Если текущая конфигурация приемлема, нажмите [RETURN] в последний раз, вследствие чего инсталляция пакета завершится. Что находится на компакт-диске? 86i
52.05 Please wait.... About To Install: qterm at /work/tools with shareable files at /work/tools/share 3lib->li±>, sbin->bin, sspool->spool, sinclude->include Are these correct [y] ? IRETURNI Proceeding with install... 0 directories added, 6 files installed, 0 symbolic links created. Approximately 99Kb installed. Сценарий инсталляции возвращается к главному меню, если вы устанавливали единственный пакет. Установив все пакеты, можно выйти из утилиты инсталляции. При этом программа предложит новую последовательность поиска (&.о7)\ You may want to change your path to include: /work/tools/bin /work/tools/share/bin Suggested new path: /work/tools/bin:/work/tools/share/bin:/bin:/usr/bin... Файл регистрации процесса инсталляции носит название /tmp/RTRinstall.log. Типичная запись в нем выглядит следующим образом: Package: jot -- installed Thu Jul 10 14:39:52 EDT 1997 /usr3/tmp/sidh/sundae/bin/jot /usr3/tmp/sidh/sundae/bin: Making directory /изгЗ/tmp/sidh/sundae/share: Making directory /изгЗ/tmp/sidh/sundae: Making directory 3 directories added, 1 file installed. Approximately 24Kb installed. Инсталляция всех пакетов с использованием стандартных установок Если вы не хотите вникать в вопросы по каждому инсталлируемому пакету и стандартные значения вас удовлетворяют, пропустите вопросы и инсталлируйте все сразу. Находясь в главном меню, наберите А, чтобы установить все пакеты: bash bitmaps bsdtar bsplit calen cpmod cvtbase delete diff ediff emacs fgrep fileutils find gawk getopt glimpse grabchars Enter the name of a package to install or choose Search, Quit or All <package>, S (earch), A(ll), Q(uit) Г Search 1 ? A IRETOFtfil Далее выводится информация о первой программе —. bash, после чего задается вопрос относительно инсталляции этой программы: GNU Bash, version 1.14.6. Bash is the GNU Project's Bourne Again Shell, an interactive shell with Bourne shell syntax (/bin/sh); but also with interactive command line editing, job control on architectures that support it, Csh-like history features and brace expansion, and a slew of other stuff. 862 Часть девятая. Разное
52.05 The bash package is approximately 2.3Mb 601Kb 383Kb 205Kb 217Kb 9Kb 741Kb 165Kb 4 9Kb - Required - Shared: 1 - Shared: '. - Shared: : - Shared: 1 - Shared: 1 -r Shared: 1 - Shared: < nstall bash [у]? у DVI Format Documentation Formatted Man pages Info files Other Documentation PostScript Format Documentation Unformatted Man pages examples/templates/extra info Стандартный ответ, обеспечивающий установку программы bash, — у. Если вместо этого ответа ввести строку ++, при инсталляции программы bash будут использованы стандартные значения без вывода запросов. Install bash [у]? ++ IRETURNI bash was compiled and made "Ready-to-Run" by Ready-to-Run Software, Inc. Copyright (c) Free Software Foundation, Inc. GNU bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later.version. GNU bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ************************ Please wait.... About to Install: bash at /usr3/tmp/sidh with shareable files at /usr3/tmp/sidh/share slib->lib, sbin->bin, sspool->spool, sinclude->include Are these correct [у]? у Proceeding with install... 14 directories added, 27 files installed, 0 symbolic links created. Approximately 2.3Mb installed. Notes (file, print, view, done) [view]? done Теперь вы можете пойти на обед. А когда вы вернетесь, то обнаружите, что в выбранном каталоге инсталлированы все программы. Альтернативное решение заключается в вызове сценария инсталляции с опцией -D, что обеспечивает применение стандартных параметров. Например: % install.pt -D Вам по-прежнему придется ответить на вопрос относительно значения umask и ввести в главном меню ответ А для инсталляции всех пакетов, однако в дальнейшем инсталляция будет произведена без каких-либо вопросов. Заметим, что для запуска процесса инсталляции со стандартными параметрами необходимо иметь право на запись в каталог /usr/local. Что находится на компакт-диске? 863
52.06 Демонтирование компакт-диска По окончании инсталляции можно демонтировать компакт-диск с помощью команды umount, указав в качестве аргумента каталог монтирования: # /etc/nraount /cdrom Что делать при наличии проблем Если программное обеспечение не устанавливается и (или) не запускается, можно позвонить по телефону технической поддержки компакт-диска (ям) компании Ready-to-Run Software. - ЕР 52.06 У вас нет дисковода компакт-дисков? Если у вас нет дисковода компакт-дисков, не отчаивайтесь. Ведь получить содержащееся на компакт-диске программное обеспечение можно и на альтернативном носителе. Об этом речь пойдет в параграфе 52.07. Однако мы все же настойчиво рекомендуем приобрести дисковод компакт-дисков, и не просто ради данного диска — компакт-диски становятся главным средством распространения информации, причем не толька программ. Приобретая дисковод, прежде всего свяжитесь с производителем вашей UNIX-системы. Необходимо убедиться, что приобретаемый дисковод совместим как с аппаратным обеспечением, так и с операционной системой. Если ваш поставщик продает собственные дисководы, он, скорее всего, порекомендует именно их. Подобное предложение можно счесть вполне разумным (при условии, что названная поставщиком цена не завышена), ведь обеспечить поддержку намного легче, если и у аппаратуры, и у программного обеспечения один поставщик. Существует и другой подход к данной проблеме — связаться с производителями дисковода и выяснить, способно ли их устройство работать на вашей платформе. Выбрать это решение следует в том случае, если вы намерены использовать один и тот же дисковод в различных системах, созданных разными производителями. Если главным ориентиром для вас является цена, ставим вас в известность, что за сотню долларов можно купить приличный дисковод компакт-дисков. С другой стороны, для дорогих устройств верхнего предела не существует... Чем дороже дисковод, тем у него лучше такие показатели, как скорость и время доступа. Если вы хотите использовать один и тот же дисковод на нескольких различных машинах, вам следует обратить внимание на внешние дисководы. Если же вы планируете применять дисковод компакт-дисков для загрузки системы, необходимо иметь такой, который может переключаться на различные размеры блока. В настоящее время большинство дисководов ориентировано на рынок персональных компьютероа Вы сможете использовать дисковод для ПК в своей UNIX-системе, но только в том случае, если он поддерживает стандарт SCSI (Small Computer Systems Interface). - ЕР, LM 52.07 Другие способы получения программного обеспечения Наряду с устройством чтения компакт-диска существует два других источника получения всех или некоторых содержащихся на компакт-диске программ. Альтернативные носители Компания Ready-to-Run Software поставляет программное обеспечение (в частности, то, которое мы поместили на компакт-диск) и на альтернативных носителях, таких как дискеты и ленточные картриджи стандартов QIC, 8мм, 4мм и DEC TK50. Некоторые из этих носителей применимы только на определенных платформах. Из-за большого объема данных каждый пакет на дискетах или ленте (в отличие от компакт-диска) предназначен только для одной платформы. Кроме того, исходные тексты и двоичные файлы дистрибутивов на дискетах рассматриваются как отдельные пакеты. 864 Часть девятая. Разное
52.07 Растровые изображения, предназначенные для использования с пакетом pbmplus (43.25), также следует заказывать отдельно. Инструкции по поводу процедуры заказа можно получить по электронной почте (info@rtr.com), а также обратившись на Web-сервер компании Ready-to-Run Software {http://www.rtr.com). Сетевой архив Сценарии и другие файлы примеров, которые рассматривались в книге, находятся в архиве свободно распространяемого программного обеспечения по адресу ftp.ora.com. (Этот архив доступен и по адресу ftp.uu.net. Многие другие компьютеры в Internet также имеют копии архивов UUNET. Если вы находитесь не в США, узнайте у своего системного администратора, нет ли в вашей части света "зеркального" сервера UUNET.) В архиве содержится не все, что есть на компакт-диске. Мы не включили туда исходные тексты программ, которые должны быть скомпилированы (szos), поскольку вы можете получать периодически обновляемые копии из сетевых архивов в Internet. В сетевом архиве отсутствуют также исполняемые двоичные файлы таких программ. Компания Ready-to-Run Software вложила много сил и средств в построение и отладку этих программ. Возьмите соответствующие двоичные файлы с прилагаемого к книге компакт-диска или с альтернативного носителя. Программному обеспечению, описанному в книге, посвящена отдельная Web-страница, которая находится по адресу http://www.ora.com (найдите там страницу, посвященную книге UNIX Power Tools). По этому адресу вы найдете полный архивный файл, ссылки на каждый сценарий в архиве, а также ссылки на многие исходные тексты. Заметим, что в указанном файле содержатся немодифицированные первоначальные исходные тексты программ без исправлений, связанных с переносимостью, которые вносит компания Ready-to-Run Software. Кроме того, версии на Web-странице и компакт-диске могут не совпадать. Надеемся, что указанная страница будет интересной для вас. Сетевой архив представляет собой tar-архив, сжатый посредством утилиты gzip. Ознакомьтесь с параграфом 19.07, чтобы узнать, как извлекать файлы из архива. Кроме того, вы можете получить отдельные сценарии и двоичные файлы (но не исходные тексты) из подкаталога split FTP-сервера. FTP Использование протокола FTP предполагает наличие машины с прямым доступом в Internet. Полный архив находится по адресу ftp://ftp.ora.com/publishecl/oreilly/power_tools/unix/upt9707.tgz. Для доступа к отдельным файлам следует обратиться по адресу ftp://ftp.ora.com/publish- ed/oreilly/powerjtools/unix/split/. Можно воспользоваться Web-броузером. В противном случае запустите UNIX-программу ftp. Приводим пример сеанса, в котором вводимые пользователем данные выделены полужирным шрифтом: % ftp ftp.ora.com Connected to ftp.ora.com. 220 FTP server (Version 6.21 Tue Mar 11 22:09:55 EST 199-7) ready. Name (ftp.ora.com:ваше_имя): anonymous 331 Guest login ok, send domain style email address as password. Password: икя@домен.com (здесь используются ваше имя и имя вашего компьютера) 230 Guest login ok, access restrictions apply. ftp> cd /published/oreilly/power_tools/unix 250 CWD command successful. ftp> binary (Очень важно! Необходимо задать передачу сжатого файла в двоичном режиме.) 200 Type set to I. ftp> gat upt9707.tgz 200 PORT command successful. 150 Opening BINARY mode data connection for uptg^O?.tgz. 226 Transfer complete. ftp> quit 221 Goodbye. % Что находится на компакт-диске? 28 9-171 865
52.07 FTPMAIL Почтовый сервер FTPMAIL доступен каждому, кто может посылать и получать электронную почту из узлов Internet. Это могут быть узлы компаний или поставщиков услуг, обеспечивающие передачу электронной почты по Internet. Вам не обязательно подключаться непосредственно к Internet. Рассмотрим, как пользоваться почтовым сервером FTPMAIL. Вы посылаете сообщение по адресу ftpmail@online.ora.com. В тексте сообщения приводите FTP-команды, которые необходимо выполнить. Сервер запустит для вас сеанс FTP и перешлет вам файлы. Чтобы получить полный справочный файл, отправьте сообщение, для которого не указана тема и которое содержит единственное слово — help. Ниже приведен пример такого почтового сеанса. Указанные команды служат для пересылки списка файлов из выбранного каталога, а также файла примеров. Список файлов пригодится, если в архиве есть более поздние версии интересующих вас примеров. % mail ftpmail@online.ora.com Subject: reply ямявдомвн.com (куда посылать файлы) open chdir /published/oreilly/power_toola/untx dir binary mime (или uuencode, если вы не используете MIME-кодировку) get upt9707.tgz quit (Эта точка — команда отправки сообщения, а не часть текста.) Допускается наличие подписи в конце сообщения после слова quit. Все извлеченные файлы делятся на части по 60 Кб и пересылаются вам. Если ваша почтовая программа поддерживает протокол MIME, она позволит объединить части сообщения и получить архивный файл. Если же вы запросили передачу сообщения в кодировке uuencode, удалите почтовые заголовки, объедините сообщения в один файл и обработайте его с помощью программы uudecode (w.os). Завершив декодирование файла и приступив к его извлечению из архива, следуйте рекомендациям, приведенным в начале данного параграфа. Для получения отдельных файлов из подкаталога split предназначена команда chdir /pub- lished/oreilly/power_tools/unix/split. Вместо режима binary установите режим ascii. Попробуйте убрать команду выбора кодировки mime (или uuencode), чтобы получить файлы без кодирования. UUCP Следующий пример демонстрирует, как по сети UUNET получать файлы с помощью программы UUCP и модема. Если вы или ваша компания зарегистрированы в UUNET, на одном из компьютеров необходимо обеспечить прямой выход в UUNET и установить программу UUCP. Найдите такую систему и введите следующую команду в одной строке (в книге команда разбита на части, поскольку не умещается в одной строке полосы набора): uuep uunet\!~/published/oreilly/power_tools/unix/upt9707.tgz имя_вашего_сервера\! ~/ваше_имя/ Символы обратной косой черты можно опустить, если вы работаете с интерпретатором Bourne shell, а не csh. Спустя некоторое время (до суток и более) файл появится в каталоге usr/spool/uucppublic/same_HMn. Если вы хотите стать зарегистрированным пользователем службы UUNET, чтобы пользоваться электронной почтой, свяжитесь с этой службой по телефону 703-204-8000. - TOR, JP 866 Часть девятая. Разное
52.08 52.08 Построение программ из исходных текстов Программы на компакт-диске поставляются в двоичной форме для самых популярных UNIX-платформ, перечисленных в параграфе 52.03. Мы поставляем также исходные тексты на языке С для тех из вас, кто работает на других платформах. Не нужно презрительно морщиться. Можно не быть программистом на С и успешно скомпилировать исходные тексты. Я никогда не писала программ на С, но все время компилирую общедоступное программное обеспечение. На компакт-диске находятся сценарии построения программ для каждого пакета, поэтому вам достаточно запустить сценарий, и полная инсталляция будет произведена автоматически. Если сценарий построения программы не работает, воспользуйтесь расположенным на компакт- диске сценарием для копирования исходных файлов на локальный жесткий диск. Хотя мы не даем гарантии, что вы сможете самостоятельно и без проблем построить программы, предлагаем вашему вниманию ряд рекомендаций по построению программ без изучения языка С. Как работает сценарий по построению программ Прежде всего попробуйте просто запустить сценарий build. Возможно, вам не придется вникать в процесс построения программы. Чтобы построить программу из исходного текста, сначала необходимо смонтировать компакт-диск, как описано в параграфе 52.05. Каждый пакет на компакт-диске, содержащий исходные тексты, может быть скомпилирован с помощью сценария построения. Этот сценарий копирует нужные файлы в текущий каталог, компилирует и инсталлирует их. Поскольку наш компакт-диск предназначен только для чтения, при построении программ вам потребуется еще один диск. Например, если вы хотите инсталлировать программу gzip, во время выполнения сценария в текущем каталоге будет создан каталог gzip. Сценарий построения имеет стандартные установки для каталога, в который копируется исходный текст; каталога, в котором выполняется инсталляция пакета; каталога, в котором инсталлируется общая часть пакета, а также префикса, добавляемого к именам общих каталогов. Эти установки могут быть изменены с помощью следующих переменных среды (s.oiy. SOURCEDIR Имя каталога, в котором находятся исходные файлы пакета, предоставляемого на компакт-диске. Сценарий построения пытается определить это имя автоматически, поэтому вам не следует устанавливать переменную SOURCEDIR, если только вы не используете сценарий построения для работы с другим носителем, а не с компакт-диском. INSTALLDIR Каталог, в котором пакет будет инсталлирован. По умолчанию — /usr/local. Чтобы изменить значение переменной INSTALLDIR, скажем, на /opt, выполните следующие команды: % satenv INSTALLDIR /opt csh % INSTALLDIR=/opt ; export INSTALLDIR sh INSTALLSHAREDIR Каталог, в котором будет инсталлирована общая часть пакета (по умолчанию — /usr/local/share). Значение этой переменной можно установить равным значению INSTALLDIR, если хотите, чтобы общая часть пакета находилась там же, где и остальная часть. SHAREPREFIX Этот префикс будет добавлен в начало имен каталогов lib, include, spool и bin. Для этой переменной не существует установки по умолчанию, но вы можете использовать s, если хотите придерживаться структуры каталогов, используемой программой инсталляции, и назвать общие каталоги slib, sinclude, sspool и sbin. Что находится на компакт-диске? 28* 867
92,08 По умолчанию файлы, генерируемые сценарием построения, не удаляются. Если перед запуском сценария установить переменную RM, весь каталог, использовавшийся при построении, будет удален по завершении работы сценария: % setenv RM true csh $ RM=true ; export RM sh Чтобы получить список имеющихся пакетов, запустите сценарий build.pt в каталоге монтирования компакт-диска. Настоящее имя этого сценария зависит от операционной системы (см. парафаф 52.05). Если исходить из того, что компакт-диск смонтирован в каталоге /cdrom, скорее всего, придется использовать одну из следующих команд: % /cdrom/BUILD.PT\;l % /cdrom/BUILD.PT % /cdrom/build.pt\;l % /cdrom/build.pt Выбранная команда выведет примерно следующий список: bash delete find bsdtar diff gawk bsplit ediff getopt calen emacs glimpse cpmod fgrep grabchars cvtbase fileutils grep Чтобы инсталлировать один из этих пакетов, запустите ту же команду, указав в ней имя пакета. Например: % /cdrom/build.pt bash Сценарий построения скопирует пакет в текущий каталог, скомпилирует его и инсталлирует согласно значениям соответствующих переменных среды. В данном примере общие и локальные части пакета объединены. % /archive/cdrom/BUILD.PT jot BUILD script provided by Ready-to-Run Software, Inc. Copyright 1997 Ready-to-Run Software, Inc. All Rights Reserved. Assuming CDROM is mounted at /archive/cdrom Assuming MACHINE is ALPHA Assuming SOURCEDIR is /archive/cdrom/SOURCES Ignore any errors about directories already existing tools/ tools/BUGS tooIs/Makefile tools/jot.с tools/lam.с tools/rs.c cc -O -o jot jot.с Недостающие программы Некоторые сценарии построения рассчитаны на то, что в системе инсталлированы нестандартные профаммы. Ряд этих программ помещен на компакт-диск: patch Некоторые пакеты зависят от профаммы patch (зз.щ, с помощью которой вносятся изменения в исходный код. Это основной метод обновления исходных текстов без замены всего файла. perl Язык Perl (37.01) используется в нескольких пакетах, в том числе в сценариях инсталляции и построения. unshar Профамма unshar необходима для извлечения архивных файлов (таз). uncompress Профамма uncompress предназначена для распаковки файлов, сжатых с помощью команды compress (24.07). 868 Часть девятая. Разное
52:08 Некоторые утилиты не включены в компакт-диск: gee GNU-версия (sj.oi) компилятора С применялась для компиляции большинства пакетов. Вы можете попробовать использовать стандартный для вашей системы компилятор С, однако он способен работать не со всеми пакетами. GNU make GNU-версия программы make (2s.is) использовалась для компиляции большинства пакетов. Если у вас нет этой программы, воспользуйтесь своей системной версией. Проблемы При построении программ возникают самые неожиданные проблемы. Может оказаться, что у вас не инсталлированы некоторые библиотеки и файлы заголовков или же компилятор работает не так, как того требует компилируемая программа. Вы можете не иметь необходимых прав доступа, а также выйти за пределы отведенного вам дискового пространства. Эти проблемы настолько разнообразны, что невозможно предложить какие-то общие способы их решения. В случае возникновения подобных проблем обратитесь к системному администратору, программисту или позвоните по телефону технической поддержки компании Ready-to-Run Software (52.щ. Тем не менее, мы можем дать несколько полезных советов по этому поводу. • Если в вашей системе возникли проблемы, связанные с именами файлов, длина которых превышает 14 символов, инсталлируйте программу bsdtar, чтобы получить возможность распаковывать tar-архивы исходных текстов. Эта программа создает уникальные 14-сим- вольные имена файлов при распаковывании архивов. • Если в вашей системе команда mkdir не поддерживает опцию -р (4.os), необходимо установить новую GNU-версию этой команды из пакета fileutils. Опция -р используется сценариями инсталляции и построения программ. А если ничего не получается, то вам остается вникнуть в исходный текст, что переносит нас в следующий параграф. Компиляция исходных текстов Для компиляции программ из исходных текстов не требуется, чтобы вы были программистами на С. Достаточно, чтобы вы имели общее представление об этой процедуре и обладали здравым смыслом (немного удачи также не помешает). Почти все двоичные UNIX-программы написаны на языке С. Эти программы сначала записываются в текстовые файлы, которые называются исходными и впоследствии с помощью компилятора преобразуются в двоичные файлы. Стандартным компилятором в UNIX является ее. Хотя многие называют ее компилятором, он в действительности выполняет все функции по построению программ, а не только компиляцию. Если не задано обратное, он сначала запускает препроцессор. Затем запускается собственно компилятор. Потом начинает работу компоновщик, создающий настоящий исполняемый файл. В данном параграфе мы опускаем все эти детали и говорим, что это делает компилятор ее. Компилятор ее обычно не вызывается напрямую: почти все программы компилируются с помощью программы make. Хотя мы не в состоянии полностью подготовить вас к компиляции программ с компакт-диска на используемой вами платформе, ознакомившись с настоящим параграфом, вы, по крайней мере, получите представление о том, как все должно происходить. Копирование исходных файлов Перед выполнением компиляции необходимо скопировать исходные тексты программ на локальный жесткий диск. Компилировать исходные тексты непосредственно на компакт-диске невозможно, так как он предназначен только для чтения. Определитесь, в какой каталог следует поместить исходные файлы, и перейдите в него. Предположим, построение программ должно осуществляться в подкаталоге начального Что находится на компакт-диске? SS9 bsdtar
52.08 каталога и необходимо инсталлировать утилиту peal. Создадим новый каталог, а затем перейдем в него: - 14.11 % mkdir ~/pcal_src % cd ~/pcal_src Для инсталляции исходных файлов на локальный жесткий диск предназначен сценарий source.pt. Если исходить из того, что компакт-диск смонтирован в каталоге /cdrom, этот сценарий может быть вызван с помощью следующих команд: % /cdrom/SOURCE.РТ\;1 иия_вакття % /cdrom/SOURCE.PT выя_пакета % /cdrom/source.pt\;l иыя_пакета % /cdrom/source.pt имя_лакетл Здесь имя_пакета соответствует имени пакета, для которого инсталлируются исходные файлы. Чтобы получить список файлов, вызовите сценарий source.pt без аргументов: % source.pt SOURCE script provided by Ready-to-Run Software, Inc. Copyright 1997 Ready-to-Run Software, Inc. All Rights Reserved. Assuming CDROM is mounted at /archive/cdrom/ Usage: /archive/cdrom/SOURCE.PT <package> Available packages are: I _enter_sh ascii bsdtar center chunksort count_it csh_init delete ediff fileutils fmt_sh getopt gzip ipl If lookfor namesort offset peal psutils res redo runsed script_tidy sharutils smiley su textutils tputinit vtree xgrep zvi 80cols _exit_csh awf bsplit cgrep cleanup count_types csh logout diff elookfor find formprog glimpse head ispell lndir ls_today netpbm oldlinks perl5 pushin rcsegrep fast relink runtime search_el shellutils squoze tar tgrep triplespace watchq xtail Clear exit_sh bash cal_today cgrep_sed cleanup_sed cpmod cvtbase dir_path emacs findemd ftpfile grabchars hey jot logerrs make_print nextday opttest phone qesh rcsgrep ren sc sedman showmatch stat tcap tknew twin whereiz zap emacs_ml addup behead calen checksed els crontab da te-mon th dirtop exrc findtext gawk grep hgrep lensort longlines manindex no_run paircheck pipegrep qsubst resrevs rename screen sh init si stree tcsh tm vgrep which zloop _enter csh age_files bkedit catsaway chmod_edit cols crush del doublespace fgrep flip getmac groff index less look motd_diff nom patch pstext qterm recomment rot screensize sh logout sis stripper termtest tpipe vis wordfreq zmore 870 Часть девятая. Разное
52.08 (Заметим, что в настоящем перечне указаны все пакеты, содержащиеся на компакт-диске: как сценарии, так и исходные тексты на языке С.) В данном примере я хочу инсталлировать утилиту peal, поэтому я запускаю следующую команду: % /cdrora/sourca.pt peal SOURCE script provided by Ready-to-Run Software, Inc. Copyright 1993- Ready-to-Run Software, Inc. All Rights Reserved. Copied /POWER_TOOLS/SOURCES/PCAL/PCAI_43.Z to pcal-4 . 3. tar. Z Copied /POWER_TOOLS/SOURCES/PCAL/RS6000/PCAL to RS6000patch.pcal Copied /POWER_TOOLS/SOURCES/PCAL/RTR/PCAL to rtrpatch.peal 3 files copied successfully. Сценарий source.pt копирует все необходимые файлы в текущий каталог. Распаковка исходных файлов Если теперь отобразить содержимое каталога, то мы увидим файлы, которые только что были скопированы туда. % is RS6000patch.pcal pcal-4.3.tar.Z rtrpatch.peal Файл pcal-4.3.tar.Z содержит пакет исходных текстов peal в сжатом архивном виде. Суффикс .Z является признаком того, что файл сжат с помощью команды compress (24.07). Сначала необходимо запустить программу uncompress: % uncompress pcal-4.3.tar.Z Если у вас нет программы uncompress, используйте gunzip. Извлечение из tar-архива После распаковки файла вы увидите его имя без суффикса .Z % is RS6000patch.pcal pcal-4.3.tar rtrpatch.peal Суффикс Лаг в имени файла pcal-4.3.1ar означает, что файл архивирован посредством программы tar (19.0S). Если у вас нет программы tar, вы найдете ее на компакт-диске. Для извлечения файла из архива предназначена команда tar с опцией х (опция / позволяет задать имя файла архива). Я также люблю использовать опцию v, которая задает вывод подробных сообщений. Моя командная строка может выглядеть следующим образом: % tar xvf pcal-4.3.tar x pcal-4,3/ReadMe, 7643 bytes, 15 tape blocks x pcal-4.3/Descrip.mms, 3713 bytes, 8 tape blocks x pcal-4.3/Make_Pcal.com, 3090 bytes, 7 tape blocks x pcal-4.3/Makefile, 1159 bytes, 3 tape blocks x pcal-4.3/Makefile.Amiga, 1010 bytes, 2 tape blocks x pcal-4.3/Makefile.DOS, 1023 bytes, 2 tape blocks x pcal-4.3/Makefile.VMS, 2505 bytes, 5 tape blocks x pcal-4.3/Orig.ReadMe, 984 bytes, 2 tape blocks x pcal-4.3/Pcal.TEX, 28199 bytes, 56 tape blocks x pcal-4.3/Pcal.hip, 29099 bytes, 57 tape blocks x pcal-4.3/SetUp.com, 670 bytes, 2 tape blocks x pcal-4.3/VaxCrtl.opt, 31 bytes, 1 tape blocks x pcal-4.3/calendar, 12101 bytes, 24 tape blocks x pcal-4.3/exprpars.c, 8538 bytes, 17 tape blocks x pcal-4.3/moon91, 2828 bytes, б tape blocks x pcal-4.3/moon92, 2887 bytes, 6 tape blocks x pcal-4.3/pcal.c, 46440 bytes, 91 tape blocks Что находится на компакт-диске? 871
52.08 x pcal-4.3/noprotos.h, 2530 bytes, . 5 tape blocks x pcal-4.3/pcalglob.h, 4551 bytes, 9 tape blocks x pcal-4.3/pcalinit.c, 4071 bytes, 8 tape blocks x pcal-4.3/protos.h, 4341 bytes, 9 tape blocks x pcal-4.3/moonphas.c, 17817 bytes, 35 tape blocks x pcal-4.3/troffman.sty, 4894 bytes, 10 tape blocks x pcal-4.3/writefil.c, 24597 bytes, 49 tape blocks x pcal-4.3/peal.man, 25296 bytes, 50 tape blocks x pcal-4.3/pcaldefs.h, 17643 bytes, 35 tape blocks x pcal-4.3/pcalinit.ps, 14297 bytes, 28 tape blocks x pcal-4.3/pcalutil.c, 21238 bytes, 42 tape blocks x pcal-4.3/pcallang.h, 35465 bytes, 70 tape blocks x pcal-4.3/readfile.c, 32888 bytes, 65 tape blocks Программа tar создает подкаталог pcal-4.3. Перейдем в него, чтобы продолжить построение программы: % cd pcal-4.3 Кстати, при наличии команды zcat можно объединить в одной командной строке шаги по распаковке архива и извлечению файлов из него (см. параграф 19.07): % zcat pcal-4.3.tar.Z | tar xvf - Если на компьютере установлена System V, необходимо использовать опцию о (19.07), чтобы стать владельцем извлеченных файлов. Извлечение из shar-apxma Вернемся на несколько шагов назад. Некоторые пакеты хранятся не как tor-архивы, а как shar-архмвы (п.02). Такие архивы, как правило, распространяются в нескольких файлах с именами, скажем, partOl.Z, part02.Z и т.д. Примером такого архива является пакет исходных файлов qterm, который распространяется как набор «Лаг-файлов. % mkdir ~/qtarm_src % cd ~/qterm_src % /archive/cdrom/SOURCE.PT qterm SOURCE script provided by Ready-to-Run Software, Inc. Copyright 1997 Ready-to-Run Software, Inc.. All Rights Reserved. . Assuming CDROM is mounted at /archive/cdrom/ Copied /archive/cdrom/SOURCES/QTERM/PARTOl.Z to partOl.Z Copied /archive/cdr6m/SOURCES/QTERM/PART02.Z to part02.Z Copied /archive/cdrom/SOURCES/QTERM/RS6000/QTERM to RS6000patch.qterm Copied /archive/cdrom/SOURCES/QTERM/I386/QTERM to I386patch.qterm Copied /archive/cdrom/SOURCES/QTERM/SUN4c/QTERM.to sun4cpatch.qterm Copied /archive/cdrom/SOURCES/QTERM/HP700/QTERM to hp700patch.qterm Copied /archive/cdrom/SOURCES/QTERM/LINUX/QTERM to linuxpatch.qterm Copied /archive/cdrom/SODRCES/QTERM/SSOL2/QTERM to SSol2patch.qterm Copied /archive/cdrom/SOURCES/QTERM/RTR/QTERM to rtrpatch.qterm 9 file(s) copied successfully. Чтобы извлечь исходные файлы пакета qterm, необходимо распаковать файлы partOl и part02, а затем вызвать программу unshar. •>. 1.16 % uncompress par to?. Z % unshar partO? unshar: Sending header to partOl.hdr. unshar: Doing partOl: If this archive is complete, you will see the following message: "shar: End of archive 1 (of 2)." shar: Extracting "README" (2200 characters) zlz Часть девятая. Разное
S2.Q8 shar: Extracting "options.3" (7383 characters) shar: Extracting "options.c" (10901 characters) shar: Extracting "options.ti" (2592 characters) shar: Extracting "qterm.c" (24777 characters) shar: End of archive 1 (of 2). You still must unpack the following archives: 2 unshar: Sending header to part02.hdr. unshar: Doing part02: If this archive is complete, you will see the following message: "shar: End of archive 2 (of 2)." shar: Extracting "Makefile" (1908 characters) shar: Extracting "qterm.1" (5805 characters) shar: Extracting "qterm.h" (3281 characters) shar: Extracting "qtermtab" (3311 characters) shar: End of archive 2 (of 2) . You have unpacked both archives. Если у вас нет программ shar и unshar, вы найдете их на компакт-диске. Заметим, что j/шг-архивы замечательны тем, что всегда можно удалить из файла все заголовки и завершающие блоки и использовать интерпретатор Bourne shell (sh) для извлечения файлов. Чтобы избавиться от заголовка, удалите все строки в начале файла, которые не соответствуют синтаксису интерпретатора Bourne shell. Поскольку многие j/шг-архивы распространяются по электронной почте или в группах новостей, файлы иногда содержат заголовки почтового сообщения или заметки. Кроме того, автор может снабдить shar-архив пояснениями относительно работы программы. Хороший способ избавиться от заголовка"— найти строку '#! /bin/sh и удалить все предшествующие ей строки. Многие архивы содержат соответствующие указания. (текст .... ) #! /bin/sh # This is a shell archive. Remove anything before this line, # then feed it into a shell via "sh file" or similar. # To overwrite existing files, type "sh file -c". При необходимости удалить завершающий блок найдите строки, напоминающие почтовую подпись пользователя. (В конце лйа^архивов обычно находится сообщение exit, поэтому не всегда требуется удалить завершающий блок... но сделать это нетрудно.^ После удаления заголовка и завершающего блока поочередно пропустите файлы через интерпретатор sh: % vi paxto? удаление заголовков % sh paxtOl; sh part02 Важно инсталлировать и распаковывать sAa/^архивы в отдельные каталоги с выразительными именами (как qtermjsrc в данном примере). Поскольку файлы shar-архта часто получают стандартные имена типа partOl, part02 и т.д., их легко перезаписать или спутать, если случайно распаковать более одного пакета в один и тот же каталог. Внесение изменений в исходные тексты Когда я скопировал исходные файлы утилиты peal с помощью сценария source.pt, то получил два файла "заплат" (ззм) с изменениями исходных текстов программ — rtrpatch.pcal и RS6000patch.pcal. Эти файлы подготовлены компанией Ready-to-Run Software для компиляции пакета peal. В файле RS6000patch.peal описаны изменения для платформы RS6000 фирмы IBM. Если вы не работаете на этой платформе, то этот файл вам не понадобится. При построении других Что находит на компакт-диске? 873
52.08 пакетов можно увидеть иные, связанные с конкретной платформой, "заплаты" с такими префиксами, как i386, xenix, hp700, sun3, sun4 и т.д. Конечно, вы не должны использовать эти пакеты, если не работаете на одной из таких платформ. В файле rtrpatch.pcal описаны изменения общего характера, применимые ко всем платформам. Прежде чем вносить изменения, необходимо убедиться, что в каталоге исходных файлов нет других файлов "заплат". Если исходные файлы поступили с файлами "заплат", содержащиеся в них изменения необходимо внести до изменений, предлагаемых компанией Ready-to-Run Software. Содержимое списка каталога pcal-4.3 следующее: % Is -aF ./ ../ Descrip.mms Make Pcal.com Makefile Makefile.Amiga Makefile.DOS Makefile.VMS Orig.ReadMe Peal.TEX Peal.hip ReadMe SetUp.com VaxCrtl.opt calendar exprpars. moon91 moon92 moonphas, noprotos. peal.с .с .с .h peal.man pcaldefs. pcalglob, pcalinit. pcalinit. pcallang. pcalutil. .h .h .c .ps .h .c protos.h readfile. troffman. writefil. ,c sty ,c Как правило, имя файла "заплаты" содержит строку patch или реп. В данном каталоге нет файлов с таким именем. Теперь, когда я удостоверился, что других "заплат" нет, можно запускать команду patch. (Если у вас нет этой команды, вы найдете ее на компакт-диске.) Перед запуском команды patch убедитесь, что вы находитесь в каталоге с исходными файлами (в данном случае — в подкаталоге pcal-4.3, созданном во время работы программы tar). Затем запустите команду patch, взяв входные данные из файла "заплаты", находящегося в родительском каталоге: < 13.21 % patch < . ./rtxpatch.pcal .■/1.21 Hmm.. . Looks like a new-style context diff to me... The text leading up to this was: I*** Makefile.orig Tue Dec 17 05:34:19 1991 I Makefile Mon Nov 23 05:59:49 1992 Patching file Makefile using Plan A... Hunk #1 succeeded at 5. Hunk #2 succeeded at 46. done Изменения внесены! Построение программ без проблем До сих пор мы занимались только тем, что собирали исходные тексты пакетов. Теперь мы вплотную подошли к этапу действительного построения пакета. Внимание! Если и существует универсальное правило компиляции исходных текстов, то вот оно: Если существует файл README, прочтите его! Обычно в файле README излагаются малоизвестные факты из истории программы и описываются внесенные улучшения. Он содержит и другие сведения. Главное то, что в нем можно найти подробности относительно инсталляции пакета. Прочитав файл README, вы сэкономите немало времени, которое часто тратится на безуспешные попытки выяснить, что предпринять, чтобы инсталлировать программу на той или иной платформе. Еще один важный файл — Configure. Этот чрезвычайно полезный файл является сценарием, который определяет, на какой платформе вы работаете и как на ней организовать инсталляцию данного пакета. Исходные файлы пакетов perl и patch поставляются со сценариями Configure. Пакет peal не поставляется с файлом README или сценарием Configure. Однако он содержит файл Makefile. (На самом деле этот пакет поставляется с несколькими файлами Makefile для различных платформ, однако стандартный файл Makefile предназначен именно для UNIX- систем, что вам как раз и нужно.) Файл Makefile используется программой make (2S./J). О программе make написаны целые тома. Главное, что вам нужно, — найти файл Makefile и набрать make, чтобы скомпилировать программу. 874 Часть девятая. Разное
52.08 Предварительно просмотрите файл Makefile и определите, есть ли в нем комментарии. Возможно, вам придется внести в файл некоторые изменения, чтобы приспособить его к своей платформе. Например, файл Makefile пакета qterm содержит следующие очень полезные строки: # # Add "-DUSG5" to DEFS below, if your system is UNIX System V. # (Добавьте "-DUSG5" в строку DEFS, если ваша система - UNIX System V.) # Add "-DHAS_VARARGS" if your system supports varargs. # (Добавьте "-DHAS_VARARGS", если ваша система поддерживает функцию varargs.) # Add "-DOPT_COMPAT" to support old command line options. # (Добавьте "-DHAS_VARARGS" для поддержки старых аргументов командной строки.) # DEFS = -DTABFILE=\"$(TABFILE)\" -DOPT_COMPAT Вы, вероятно, знаете, основана ваша система на System V или нет. Если вы никогда не использовали эту программу, то, скорее всего, вас не интересуют старые опции командной строки. И если вы не знаете, что такое varargs и поддерживает ли ее ваша система, попытайтесь найти соответствующие разделы интерактивной документации (so.oi), % man varargs VARARGS(3) С LIBRARY FUNCTIONS VARARGS(3) NAME varargs - handle variable argument list SYNOPSIS #include <varargs.h> Оказывается, у меня есть функция \агат&. Поэтому я добавлю опцию командной строки -DHAS_VARARGS в строку DEFS=: DEFS = -DTABFILE=\"$(TABFILE) \" -DOPT_COMPAT -DHAS_VARARGS Файл Makefile пакета peal содержит только одну строку инструкций: # Set the configuration variables below to taste. # (Установите по своему усмотрению переменные конфигурации.) Это не очень полезная инструкция, однако просмотрите файл Makefile и найдите там установки, которые кажутся вам неправильными. Убедившись, что все нормально, пошевелите пальцами и запустите программу make. % make /bin/cc /bin/cc /bin/cc /bin/cc /bin/cc /bin/cc pcalinit /bin/cc /bin/cc writefil, -c peal.с -с exprpars.c -с moonphas.c -с pcalutils.c -с readfile.c -о pcalinit pcalinit.с pcalinit.ps pcalinit.h -c writefil.с -о peal peal.о exprpars.o moonphas.o pcalutil.o readfile.c- .o -lm Сообщений об ошибке и предупреждений не было, поэтому все в порядке. Ошибки свидетельствуют о том, что инсталляция пакета выполнена не полностью. В этом случае нужно найти причину ошибки и устранить ее. Если вы получали предупреждающие сообщения, то программы могут работать неправильно или с небольшими погрешностями. Что находится на компакт-диске? 875
5K0F При инсталляции, таких программ, как электронные таблицы, в которых могут быть скрытые погрешности, следует вникать в смысл предупреждений и ликвидировать причины их возникновения. Если инсталляция прошла нормально, еще раз пошевелите пальцами и проверьте, работает ли программа. Если теперь вывести содержимое каталога исходных файлов пакета peal, то можно заметить несколько новых имен файлов с суффиксами .о, но главное то, что создан исполняемый файл peal. % Is -aF ./ ../ Descrip.mns Make Pcal.com Makefile Makefile.Amiga Makefile.DOS Makefile.VMS Makefile.orig Orig.ReadMe Peal.TEX Peal.hip ReadMe SetUp.com VaxCrtl.opt calendar exprpars.с exprpars .0 moon91 moon92 moonphas. moonphas, moonphas, peal* peal.c peal.man pcal.o . с .0 .h pcaldefs.h pcalglob.h pcalinit* pcalinit.c pcalinit.h pcalinit.ps pcallang.h pcallang.c pcallang.o protos.h readfile. readfile. troffman. writefil, writefil. .c .0 . sty . с ,o Теперь можно попытаться запустить программу и, убедившись, что она работает, инсталлировать ее. Для инсталляции программы многие файлы Makefile вызываются с опцией install. % make install На многих машинах вам необходимо зарегистрироваться как пользователь root, чтобы иметь возможность инсталлировать доступные для всей системы двоичные файлы и разделы документации. В таком случае неплохо сначала запустить программу make с опцией -п. Эта опция позволяет определить, какие команды будут функционировать корректно, не запуская эти команды. % make -n install Если же вы предпочитаете инсталлировать программу вручную, переместите исполняемый файл и файл документации в нужный каталог (не забудьте соответствующим образом переименовать последний файл): % mv peal /usr/local/bin % mv pea.man /usr/local/man/manl/pcal.l Заметим, что' некоторые программы требуют выполнения дополнительных действий при инсталляции исполняемого файла. Если все работает как предполагалось, можно расслабиться. В противном случае необходимо больше узнать о происходящих процессах, чтобы выяснить, что сделано неправильно. Функции, библиотеки и файлы заголовков Чтобы понять процесс компиляции, полезно ближе познакомиться с библиотечными файлами и файлами заголовков. Программы на С почти целиком пишутся с использованием функций. В параграфе 15.03 приведен пример функции, написанной на языке Bourne shell. При построении функции языка С используется, как правило, та же идея: надлежит объединить цепочку команд, дать ей имя, а затем выполнять эти команды, используя присвоенное имя, когда угодно и сколько угодно раз. Теперь вы можете определить функции языка С в одном исходном файле. Операционная система предоставляет обширную коллекцию определений функций, что очень удобно, поскольку в противном случае вам пришлось бы строить каждую программу с нуля. Определения функций содержатся в библиотеках, которые, как правило, инсталлированы в вашей системе в каталоге /usr/lib/ и имеют имена с префиксом lib и суффиксом .а (например, /usr/lib/libc.a). Функции должны быть объявлены в программе. Эти объявления содержатся в файлах заголовков, которые, как правило, инсталлированы в вашей системе в каталоге /usr/include/ и имеют имена с суффиксом .А (например, /usr/include/stdio.h). 876 Часть девятая. Разное
SZMl Если вы используете функции, определенные в библиотеках (что характерно для большинства случаев), после компиляции программы необходимо связать с ней нужные библиотеки. Следует также обеспечить, чтобы ваша программа читала соответствующие файлы заголовков, поскольку программа не будет компилироваться, пока не объявлены все функции. Например, для вычисления квадратного корня числа в программе используется функция sqrtQ. Данная функция находится в математической библиотеке. Это значит, что необходимо связать программу с библиотекой libm.a, а также прочесть файл заголовков math.h (в котором объявляется функция sqrtQ). Поэтому в программе в начале исходного файла должна находиться следующая строка: #include <math.h> При компиляции программы в командной строке нужно указать опцию -/, чтобы обеспечить связывание с библиотекой: % ее -о файл файл.с -lm Отметим следующие моменты: • Если не присвоить файлу имя с помощью опции -о файл, компилятор ее назовет его a.out. • Имя исходного файла должно заканчиваться суффиксом .с. • Поскольку файл math.h находится в каталоге /usr/include, в строке # include не нужно указывать его полное путевое имя, достаточно указать имя файла в угловых скобках, как показано в примере. В угловых скобках можно задавать относительные путевые имена (U.02), начинающиеся с /usr/include. Например, <sys/foo.h> — это то же, что и /usr/include/sys/foo.h. По умолчанию компилятор се ищет файлы заголовков в каталоге /usr/include. Указав опцию -/ в командной строке, можно задать автоматический поиск в другом каталоге. Если необходимо использовать файл заголовков в каталоге, который не просматривается по умолчанию, заключите его полное или относительное путевое имя в двойные кавычки. • При связывании с библиотекой, указанной в командной строке, в конце строки необходимо поставить опцию -/. Если библиотек несколько, следует использовать несколько таких опций. Важно придерживаться определенного порядка следования опций -/; просмотрите документацию или найдите соответствующий комментарий в исходном тексте. • Компилятор находит файл libm.a, потому что он расположен в каталоге /usr/lib, который просматривается по умолчанию. Если необходимо использовать библиотеку из другого каталога, следует указать ее в командной строке с помощью опции -L. Как вы догадались, это далеко не все, что нужно было бы знать. Мы предоставили лишь общую схему процесса компиляции программ на С в UNIX-системах, и это ровно столько, сколько мы можем рассказать, не обучая вас языку С. Программа make Простую программу на С несложно скомпилировать с помощью компилятора ее: % ее test.с Компиляция более сложных программ (например, многих программ на компакт-диске) требует дополнительных усилий. С такими программами проще работать, если создавать их в отдельных модулях, или с-файлах. Так, например, пакет исходных текстов peal на компакт- диске содержит несколько с-файлов: exprpars.c, moonphasx, pcalinit.c, pcalutil.c, readflle.c, writefll.c и, конечно, pcal.c. Каждый исходный файл необходимо отдельно скомпилировать в объектный файл (с суффиксом .о). Если задать опцию -с, компилятор ее скомпилирует исходный код в объектные файлы и завершится без создания готового исполняемого файла. При повторном запуске с указанием имен объектных файлов (exprpars.o, moonphas.o и т.д.) компилятор свяжет все эти файлы с библиотеками и создаст исполняемый файл. Все это затрудняет управление процессом компиляции, так как он предполагает выполнение нескольких дополнительных действий. Кроме того, из этого следует, что если какой-либо файл изменяется, то вам придется не только перекомпилировать его, но и повторно связать с остальной программой. Что находится на компакт-диске? 877
52.09 Эта как раз задача для программы make. Мы демонстрировали применение этой программы в параграфах 21.09 и 28.13, но те примеры были рассчитаны на простые ситуации. Пакет исходных текстов peal поставляется вместе с файлом Makefile. (На самом деле он поставляется с несколькими файлами Makefile для различных платформ, но это уже другая тема.) В этом файле отслеживаются все программы со всеми их зависимостями. В нем также учтены все опции командной строки, которые могут передаваться компилятору ее, а также используемые библиотеки. В результате все, что необходимо сделать для создания программы peal, — набрать следующее: % make peal или еще проще: % make Это более простой способ, чем самостоятельное отслеживание всех модулей и опций командной строки. Так, если программу невозможно скомпилировать из-за того, что необходимый ей файл заголовков установлен в нестандартном месте, это необходимо отразить в файле Makefile. Это делается путем добавления опции -/ в строку объявления переменной COPTS: COPTS = -I/usr/include/sys Чтобы вместо се использовать другой компилятор, переопределите переменную СО. СС = /usr/local/bin/gcc Это лишь вершина айсберга. Однако базовые знания о библиотеках, файлах заголовков и программе make помогли мне построить многие программы, которые поначалу не компилировались. - LM, ЕР 52.09 Поддержка программного обеспечения от компании RTR Компания Ready-to-Run Software (которая подготовила исполняемые файлы, сценарии построения и "заплаты" для данной книги) обеспечивает поддержку программного обеспечения, поставляемого на прилагаемом компакт-диске (или другом носителе). Компания предоставляет консультации по • использованию пакетов; • компиляции и построению пакетов; • инсталляции; • переносу на другие платформы. У вас может сложиться впечатление, что раз программное обеспечение бесплатно, то и консультации тоже предоставляются бесплатно! Это мнение ошибочно. Ведь на консультации необходимо тратить время, особенно если речь идет о таком широком наборе пакетов, какой содержится на компакт-диске. Консультации предоставляются по телефону технической поддержки компакт-диска компании Ready-to-Run Software: (900) 555-UNIX (8649). Цена — $2.95 за минуту. Вы можете заключить соглашение о консультациях по фиксированной цене. Напишите или отправьте запрос о ценах по факсу. (Цена зависит от используемой платформы и поддерживаемого пакета.) Приводим адрес: Ready-to-Run Software, Inc. 4 Pleasant Street Forge Village, MA 01886 (978) 692-9990 (факс) - JM 878 Часть девятая. Разное
53 Словарь терминов Аргумент (argument) Строка символов, которые передаются программе как одно целое. Интерпретатор shell разбивает командную строку на аргументы, используя в качестве разделителей пробельные символы, не заключенные в кавычки. См. также параграф 8.05. Библиотечные функции (library functions) Пакеты системных вызовов (или других библиотечных функций) для программистов. Чаше всего библиотечная функция является операцией более высокого уровня, чем системный вызов. См. также Системный вызов. Буфер (buffer) Место временного хранения данных, например файл или область памяти компьютера. Большинство текстовых редакторов хранят редактируемый текст в буфере. При сохранении содержимое буфера записывается поверх исходного файла (т.е. заменяет его). Ввод-вывод (I/O) Ввод или вывод данных из программы или аппаратного устройства. Восьмеричное число (octal number) Число в системе счисления с основанием 8. Восьмеричные числа состоят из цифр от 0 до 7. Например, десятичное (с основанием 10) число 12 соответствует восьмеричному числу 14. ASCII-коды символов (s/.oj) часто представляются в виде восьмеричных чисел. Временная характеристика (timestamp) Файловая система UNIX запоминает, когда файл был изменен или прочитан в последний раз или когда был изменен его индексный дескриптор. Эти значения времени, особенно время изменения, часто называют временными характеристиками. См. также параграф 21.13. Выбор по умолчанию (default) В программе, предоставляющей возможность выбора из нескольких вариантов, вариант по умолчанию принимается тогда, когда пользователь ничего не указывает. Вариант по умолчанию обычно является наиболее часто употребляемым. Например, по умолчанию входным файлом многих UNIX-программ является стандартный ввод. Если не задано имя файла, программа читает данные из стандартного ввода. Двоичный файл (binary file) Файл, содержащий нетекстовые символы. Чаще всего это непосредственно исполняемые файлы, которые можно запускать в качестве программ. Для кодирования двоичных символов используются все 8 бит байта. См. также ASCII-файл. Двойная кавычка (double quote) Символ ". Этот символ не соответствует двум одинарным кавычкам (' '). В двойные кавычки заключают часть командной строки UNIX, в которой интерпретатор shell должен произвести Словарь терминов 879
подстановку переменных или результатов выполнения команд (в интерпретаторе С shell еще и подстановку из перечня ранее введенных команд), но не выполнять другую интерпретацию. См. также параграфы 8.14 и 8.15, Одинарная кавычка. Целение по модупю (modulo) Вспомните уроки арифметики в школе. При делении двух чисел мы имеем делимое (в числителе), делитель (в знаменателе), частное (целочисленный результат) и остаток (то, что остается). Если нас интересует остаток, а не частное, мы выполняем операцию деления по модулю (modulus, modulo, mod). Вот пример такой операции: 13 : 3 = 4 (с остатком 1) Операция деления по модулю записывается следующим образом: 13 mod 3=1 Слово modulo используется также в выражениях типа "modulo wildcards", что означает "все, кроме метасимволов". Демон (daemon) Невидимая пользователю программа, выполняющая важные системные функции — от управления подкачкой и сетевого обмена до обработки сообщений о поступлении почты. В BSD UNIX насчитывается около двадцати демонов. Большую часть времени демоны "спят", ожидая какого-либо события, поэтому они не загружают процессор. См. также параграф 1.14. Жестко заданный (hardcoded) Данное определение говорит о том, что значение, о котором идет речь, нельзя изменить. Например, в сценарии интерпретатора shell, содержащем команду grep jane, значение jane задано жестко: команда grep всегда будет искать слово jane. А вот в команде grep $USER искомый текст не задан жестко — это переменная. Задание (job) Отдельная команда UNIX. Часто по незнанию термины "задание", "процесс" и "программа" используют как синонимы. Однако, согласно документации по UNIX, термин "задание" следует употреблять для обозначения одной-единственной командной строки. Заметим, что командная строка может быть достаточно сложной. Например, строка pic a.ms | tbl |eqn I troff -ms является одной командой, следовательно, одним заданием, состоящим из четырех процессов. . Зомби (zombies) "Мертвые" процессы, которые еще не удалены из таблицы процессов. Обычно процессы-зомби исчезают мгновенно. Однако иногда не удается удалить такой процесс из таблицы, и он остается в ней (и выводится по команде ps) до перезапуска системы. Кроме места в таблице процессов, зомби не занимают никаких системных ресурсов. См. также параграф 38.16. Идентификатор процесса (PID) При запуске каждого процесса UNIX-система присваивает ему идентификационный номер — PID (process identifier). Впоследствии этот номер позволяет ссылаться на данный процесс. Если выполняющуюся программу нужно уничтожить (38.W), обращение к ней производится по идентификатору ее процесса. Вывод идентификаторов процессов осуществляет команда Р£ (38.05). См. также параграф 38.03. Индексный дескриптор (mode) Структура данных, описывающая файл. В любой файловой системе количество индексных дескрипторов и, следовательно, максимальное количество файлов задается при ее создании. См. также параграф 1.22. 880 Часть девятая Разное
Интерпретатор shell Программа, которая читает и интерпретирует командные строки, а также выполняет указанные команды. См. также параграфы 8.05 и 44.03. Кавычка (quote) См. "Обратная кавычка, Двойная кавычка, Одинарная кавычка. Канал (pipe) Механизм, применяемый в UNIX для передачи выходных данных одной программы на вход другой программы без использования промежуточного файла. Все UNIX-системы поддерживают каналы. Операционные системы System V и SunOS предоставляют также "именованные каналы", являющиеся буферами типа FIFO (first-in/first-out — первым вошел, первым вышел), к которым можно обращаться по имени. См. также параграф 1.04. Каталог bin Каталог для хранения исполняемых программ. См. также параграф 4.02. Код режима доступа (mode) В UNIX — восьмеричное число, описывающее, какой тип доступа к файлу имеют владелец файла, группа пользователей и остальные пользователи. См. также параграф 1.23. Командная строка (command line) Текст, который вводится по приглашению интерпретатора shell. Интерпретатор читает командную строку, анализирует ее, отыскивает в ней имя команды (которое является первым словом командной строки) и выполняет эту команду. Командная строка может содержать несколько команд, объединенных такими операторами, как точка с запятой (;) (s.os), символ канала (I) (l.M) и двойной амперсанд (&&) (44.09). Коммутатор (data switch) Это устройство похоже на телефонный коммутатор и соединяет несколько терминалов с двумя и более компьютерами. Пользователь через терминал или модем сообщает коммутатору, к какому компьютеру он хочет подключиться. Другое название — мультиплексор терминалов. При отсутствии коммутатора компьютеры обычно имеют по одному терминалу, подключенному к каждому порту /Q> (з.му, такие параметры, как тип терминала (s.io), задаются в системных файлах. С другой стороны, компьютеры с коммутатором не могут определить заранее, терминал какого типа будет подключен к каждому порту tty. Косая черта (slash) Символ /. Он разделяет элементы в строке указания пути к файлу. См. также параграф 1.21, Обратная косая черта. Массив (array) Упорядоченный набор данных. Массив имеет имя; каждый его член называется элементом массива. Например, интерпретатор С shell сохраняет последовательность поиска команды в массиве (47.os> с именем path (t.osy Первый элемент массива называется $path[l], второй — $path[2] и т.д. Негативное изображение (reverse video) Обращение цвета фона и изображения на экране компьютера. Данный режим используется для выделения определенной области или обозначения текста, который должен быть использован или модифицирован. Например, если текст обычно отображается черными буквами на белом фоне, то в режиме негативного изображения он будет представлен белыми буквами на черном фоне. См. также параграф 5.08. . дэдг'|- Словарь терминов 881
Номер задания (job number) Интерпретаторы shell, имеющие механизм управления заданиями, присваивают номер каждой остановленной или выполняемой в фоновом режиме (1.26) команде. Номера заданий можно использовать для создания ссылок на собственные команды или группы команд. Следует отметить, что в работе удобнее применять номера заданий, а не идентификаторы процессов: они намного меньше (обычно от 1 до 10) и поэтому легко запоминаются. Команда jobs интерпретатора С shell выводит номера выполняемых заданий. См. также параграф 12.01. Номер индексного дескриптора (i-number) Файл в UNIX имеет имя (понятное пользователю) и номер индексного дескриптора (используемый ядром). Каждый номер индексного дескриптора хранится в каталоге вместе с именем файла, чтобы UNIX могла легко найти файл, заданный по имени. См. также параграф 1.22. Обратная кавычка (backquote) Символ '. Этот символ не соответствует одинарной кавычке (')• Используется для подстановки результатов выполнения команды (9.16). Обратная косая черта (backslash) Символ \. В UNIX он определенным образом изменяет интерпретацию последующего символа. См. также параграф 8.20, Косая черта. Одинарная кавычка (single quote) Символ '. Этот символ не соответствует обратной кавычке (*). Одинарные кавычки используются для выделения части командной строки UNIX, в которой shell не должен производить интерпретацию (С shell выполняет только подстановку из перечня ранее введенных команд). См. также параграфы 8.14 и 8.15, Двойная кавычка. Пакетная обработка (batch queue) Механизм последовательного выполнения большого количества заданий. Задания помещаются в очередь по мере поступления запросов от пользователей и выполняются в порядке очередности. Возможность пакетной обработки появилась в эпоху становления компьютерных систем. Она является чрезвычайно эффективным, хотя и не очень удобным средством для оптимизации работы системы. См. также параграф 40.06. Паника (panic) Жаргонизм, применяемый в UNIX для обозначения катастрофической ситуации. Паника возникает, когда UNIX обнаруживает неустранимое нарушение целостности в одной из внутренних структур данных. В этом случае ядро останавливает работу системы, чтобы избежать серьезных разрушений, а на экран выводится соответствующее сообщение. Переключатель (option switch) Помещается в командную строку для изменения способа выполнения команды. Обычно начинается с дефиса (-). Различие между понятиями "опция" и "переключатель" связано с тем, что у опции может быть более двух значений, а переключателю всегда соответствуют два значения: включен/выключен, доступен/недоступен, да/нет и т.п. Переносимая программа (portable program) Программа, которая может быть использована более чем в одной версии UNIX или более чем с одним интерпретатором shell. Подкатапог (subdirectory) Каталог, находящийся в другом в каталоге. См. также параграфы 1.21 и 4.07. Подкачка (swapping) Механизм, используемый ядром UNIX для освобождения физической памяти. Ядро перемещает процесс (образ программы) из памяти на диск, а затем предоставляет память другому 882 Часть девятая. Разное
процессу. Процесс, который был неактивным в течение определенного времени, может быть удален из памяти в целях экономии ресурсов. Подкачка используется также в случае острой нехватки памяти. Когда система испытывает недостаток в памяти, активный процесс может быть выгружен из памяти. Полный цуплекс (full duplex) Обмен информацией между терминалом и компьютером, при котором данные пересылаются в обоих направлениях одновременно. При полудуплексном (half duplex) обмене одновременная пересылка данных сразу в двух направлениях невозможна. В настоящее время такой вид обмена используется редко. См. также параграф 41.02. Последовательность поиска (search path) Список каталогов, которые интерпретатор shell просматривает в поисках программного файла, подлежащего выполнению. См. также параграфы 6.04 и 8.07. Приглашение (prompt) Программа запрашивает информацию, выводя на экран короткую строку типа Delete файл? и ожидая ответа. См. также Приглашение интерпретатора shell. Приглашение интерпретатора shell (shell prompt) Сигнал от интерпретатора shell (при работе в интерактивном режиме), свидетельствующий о том, что интерпретатор готов прочесть командную строку. По умолчанию в интерпретаторе С shell приглашением является знак процента (%), а в интерпретаторе Bourne shell — знак доллара ($). Приоритет (priority) Число, определяющее, насколько часто ядро будет запускать процесс. Процесс, имеющий более высокий приоритет, запускается чаще и, следовательно, завершается быстрее, чем процесс с низким приоритетом. Пробельный символ (white Space) Один или несколько символов пробела или табуляции. Процесс, (process) Процесс — это не что иное, как еще одно название программы, выполняющейся в системе. Существует и другое определение: процесс является отдельным потоком выполнения или отдельным тГотоком инструкций компьютера. Одно задание может состоять из нескольких различных процессов. Например, командная строка, содержащая каналы ом), может запускать два.-и более процесса. Си. также параграф 38.03. .-'Псевдокод (pseudo-code) Способ записи текста, при котором он структурирован как текст программы, но при этом не используется какой-либо язык программирования. Псевдокод обычно применяется для пояснения программы. Пустая строка (null) Пустая строка без символов, длина которой равна нулю. Это не то же самое, что ASCII-символ NUL (51.03). Раздел (partition) Часть диска. Обычно диски в UNIX имеют восемь разделов, хотя не обязательно все они используются. Размер блока (block size) Наибольший объем данных, которые файловая система UNIX размещает в виде непрерывного блока. Так, если размер блока файловой системы равен 8 Кб, то файлы размером до 8 Кб физически непрерывны (т.е. находятся в одном месте), а не разбросаны по диску. Файлы, Словарь терминов 883
которые по размеру больше блока файловой системы, разбиваются на фрагменты по 8 Кб и размещаются в разных местах диска. Фрагментация ограничивает быстродействие файловой системы. Обратите внимание, что размер блока файловой системы отличается от размера физического блока диска, который почти всегда равен 512 байтам. Рекурсия (recursion) Термин, относящийся к программе или подпрограмме, которая повторно вызывает саму себя или многократно выполняет одни и те же действия. Например, программа find (izoi> рекурсивно проходит по дереву каталогов, выполняя определенную операцию в каждом каталоге. Сетевая файповзя система (NFS) Сетевая файловая система (Network File System) позволяет системам UNIX и многим отличным от них системам совместно использовать файлы посредством сети с протоколами TCP/IP. Несмотря на некоторые ограничения, связанные с вопросами безопасности, NFS обеспечивает полный доступ к файлам других компьютеров. См. также параграф 1.33, TCP/IP. Сеть (Net) Данный термин применяется в настоящей книге для обозначения сетей Usenet и Internet (из). Симвоп новой строки (newline) Символ, обозначающий конец текста в большинстве UNIX-систем. (Это соглашение, а не требование). Синтаксический анализ (parte) Заключается в разделении некоторого программного кода на части с их последующей интерпретацией. В парафафе 8.05 объясняется, каким образом интерпретатор shell анализирует командную строку. Системный вызов (system calf) Доступ к операционной системе UNIX на самом низком уровне. Все функции в UNIX используют системные вызовы. См. также Библиотечные функции. Скрытые файлы (cshrc, .login, .profile...) Файлы, которые читаются при запуске профаммы (а также при входе в систему и запуске интерпретатора shell). Эти файлы инициализируют среду, а также выполняют любые другие команды UNIX (например, tset). Если в вашей учетной записи задано использование интерпретатора С shell, будут читаться файлы .cshrc и .login. Если же в учетной записи задано использование Bourne shell или подобного ему интерпретатора, будет читаться файл .profile. См. также парафаф 2.02. Слово (word) Единица информации, состоящая из одного или нескольких символов. В UNIX слова могут содержать пробельные символы. Существуют также слова нулевой длины, которые не содержат ни одного символа. См. также Строка. Специальный файл (special file) Элемент файловой системы, осуществляющий доступ к устройствам ввода-вывода. Существуют специальные файлы для каждого терминала, каждого сетевого контроллера, каждого раздела диска и каждого устройства доступа к ленточному накопителю. См. также парафаф 1.29. Средняя загруженность (load average) Показатель загруженности процессора. Этот показатель полезен, хотя и неточен. Его значение вычисляется путем суммирования среднего количества выполняющихся заданий и среднего количества блокированных заданий, ожидающих выполнения дисковых операций ввода-вывода. Команда uptime (39.07) выводит значение средней зафуженности. Строка (string) Последовательность символов. См. также Слово. 884 Часть девятая. Разное
Управляющий символ (control character) Символ, который вводится-.при нажатии клавиши [CTRL] в.сочетании с клавишей алфавитно-цифрового блока клавиатуры. Усечение (truncation) Сокращение чего-либо. Например, "усечь файл после строки 10" означает удалить все строки после строки с номером Ю- Файл дампа (core file, core dump) Файл, называемый дампом, возникает при ненормальном завершении программы. Файл дампа используется для отладки. См. также параграф 24.05. Файловая система с доступом только для чтения (read-only filesystem) Файловые системы обычно инсталлируются таким образом, чтобы запись данных была разрешена только тем пользователям, которые обладают соответствующими правами (i.2iy Системный администратор может смонтировать файловую систему с доступом только для чтения. Тогда ни один пользователь не сможет произвести в ней изменение файлов. Флаг (flag) В программировании переменные-флаги используются для указания на то, что определенное событие произошло или что нужно выполнить некоторое действие. Например, флаг может быть установлен, если пользователь ввел что-то неправильно. Программа проверит этот флаг и не возобновит работу до тех пор, пока проблема не будет решена. Фрагмент (fragment) В "быстрой файловой систем?" BSD — часть дискового блока (обычно — восьмая часть, но в некоторых случаях — четверть или половина блока). Если последняя часть файла не занимает полный дисковый блок, файловая система вьщеляет для нее один или несколько фрагментов, а не целый блок. Не путайте "фрагмент" с "фрагментацией". Фрагменты позволяют файловой системе BSD использовать, дисковые .-блоки больших размеров без снижения, эффективности. Хэш-таблица (hash table) Хэширование данных, т.е. преобразование их в формат хэш-таблицы, позволяет специально разработанным, программам осуществлять быстрый поиск информации. Хэш-таблица хранит специальный код поиска для каждого элемента данные Например, интерпретатор С shell использует хэш-таблицу для ускорения поиска команд. Команда rehash (4.02) перестраивает хэш-таблицу после добавления в систему новой команды. Эмулятор терминала (terminal emulator) Программа, которая заставляет монитор компьютера эмулировать (работать как) терминал определенного типа. Многие такие программы эмулируют терминал Digital Equipment Corporation VT100. ' .-•-.-■•-- •■■:.- ■■---. Ядро (kernel) Часть операционной системы UNIX, обеспечивающая управление памятью, обслуживание ввода-вывода и выполнение других низкоуровневых функций. Ядро является "сердцем" операционной системы. См. так'же параграф 1.14. ш Версия UNIX корпорации IBM. ASCII-файл Файл, содержащий символы ASCII (Si.oj). В более широком смысле (по крайней мере, в США) — файл/ содержащий текст, который можно вывести на печать или на экран и в котором отсутствуют "двоичные" (не-ASCII) символы. Для кодирования каждого ASCII-символа используются только 7 бит. Словарь терминов 885
BSD UNIX Версии UNIX, разработанные в Калифорнийском университете в Беркли. BSD UNIX доминировала в университетской среде. В плане возможностей она превосходила System V: BSD была первой системой, в которой появились такие функциональные возможности, как виртуальная память, работа в сети и "быстрая файловая система". Именно на этой системе базируется SunOS. System V Release 4 и ряд ее более ранних версий обладают возможностями, которые присущи BSD. CTRL-x Символ, вводимый при нажатии комбинации [CTRL+x], где х — клавиша на клавиатуре. См. также Управляющий символ. Escape-символ Символ, который расположен перед другим символом или строкой символов и изменяет способ их интерпретации. Escape-символ может как отменять специальное назначение символов (например, при использовании кавычек и обратной косой черты (S.i4)), так и устанавливать его (как в терминальных Escape-последовательностях (s.osj). Flame Эмоциональное выражение, на жаргоне — "наезд". Free Software Foundation, FSF (Организация по разработке бесппатных программ) Группа, разрабатывающая свободно распространяемое программное обеспечение. Ее адрес — 675 Massachusetts Avenue, Cambridge, MA 02139 USA. GNU "Gnu's Not Unix" — система программного обеспечения, которая со временем должна стать бесплатной альтернативой UNIX. См. также Free Software Foundation. Gotcha Ловушка, трудность или неожиданность, возникающая при работе программы, на жаргоне — ГЛЮК . P0SIX "Открытая" операционная система, подобная UNIX. SCSI "Small Computer Systems Interface" (интерфейс малых компьютерных систем) — стандартный интерфейс для дисковых и ленточных накопителей, который в настоящее время используется во многих UNIX- (и не UNIX) системах. System V UNIX Версия UNIX фирмы AT&T. Самой последней версией System V является выпуск 4, известный также как V.4 или SVR4. TCP/IP Набор сетевых протоколов, которые обычно применяются для передачи данных в среде Internet. Часто используются в выделенных линиях для удаленной связи. termcap Сокращение от terminal capabilities (параметры терминала). База данных, содержащая характеристики терминала и представляющая собой старый (и все еще используемый) способ описания терминалов в UNIX. См. также параграф 41.11, termlnfo. terminfo База данных, содержащая характеристики терминала и представляющая собой более новый способ описания терминалов в UNIX. См. также параграф 41.11, termcap. 886 Часть девятая Разное
uuencode, uudecode Утилиты, первая из которых кодирует файлы с двоичными (8 бит) символами в ASCII-формат (7 бит), а вторая — декодирует их в исходный двоичный формат. Эти утилиты используются при передаче данных по каналам связи, которые не поддерживают передачу двоичных (8-битовых) данных. См. также параграф 19.05. VAX/VMS Популярная операционная система фирмы Digital Equipment Corporation. ЛЕМЙГ Одна из первых версий UNIX для компьютеров IBM PC и одна из немногих версий, работающих на системах 80286. XENIX происходит от UNIX Version 7, разработанной фирмой AT&T в конце 70-х. Она имеет много общего с BSD UNIX. Впоследствии XENIX была преобразована в System V.2. - JP, ML
Предметный указатель Аргументы командной строки 746 Архивы: — в shell 252, 291, 293, 294 — разархивирование 293, 294 — сжатые: — извлечение файлов 296 — создание 294 База данных: — для поиска 267 — терминала 74 — termcap 75, 77, 80, 81, 83-85, 88, 99, 101, 321, 340, 482, 572, 645, 656, 657, 663, 665, 668, 669, 671, 677, 832, 837, 848, 851 -^ кодирование аргументов 662 — коды специальных символов 661 — формат 658 — terminfo 75, 80, 83-35, 88, 99, 101, 321, 572, 645, 656, 657, 669, 677, 832, 848 — кодирование аргументов 662 — коды специальных символов 661 — формат 658 — zoneflles '. 91 Буфер: — обмена 454 — пронумерованный 457 Буферизация 678 — каталог 683 В Ввод-вывод: — переадресация 42, 94, 194, 377, 422 — стандартное устройство 194 Вектор слов 120 Виртуальная память 619 Временная характеристика 234, 259, 318 Время: — в UNIX-системах 614 — вычисление в системе 828 — демон времени 828 — изменения индексного дескриптора 317 — операций ввода-вывода 619 — полное 614, 618 — пользовательское 614, 618 — последнего доступа 234, 317 — последней модификации 231, 234, 247, 317, 433 — работы в сети 619 — режим разделения 622 — системное 614, 618 — системные часы 828 — среднее 617 — такт 828 — эпоха 828 Выгрузка страниц 627 Г Генератор случайных чисел 743 д Деловая графика. 813 Демоны _.. 25 Дерево каталогов 243 — визуализация 244 — дублирование 265 — копирование 287, 289, 294 — перемещение 255 — сжатие > 364, 369 — схема 211 Дескриптор файла 749, 779 — закрытие 779 — манипулирование 738 Дисковая квота 341, 351, 373 — жесткий лимит 373 — мягкий лимит 373 Документация, интерактивная _ 814 — разделы 814, 818 — создание 822 — файлы _ 818 Дуплекс, полный 646 ♦ 3 Загруженность системы 105, 621 — средняя 621 Задание: — автоматический повторный запуск 635 — запуск в фоновом режиме 39 — изменение приоритета 625 — изменение состояния 190 — интерактивное 186, 600, 625 — уничтожение 606 — останов 190, 487, 666 — отложенное выполнение 630 — пакетная обработка 633 — периодическое выполнение 637, 638 — прерывание 487 Предметный указатель 889
— текущее 189 — удаление 635 — управление 21, 39, 134, 186, 189, 191, 192, 342, 470, 599-601, 609, 611, 830 — фактор уступчивости 623 — фоновое 40, 105, 186, 191, 600 Значки эмоций 835, 851 Зомби 607, 612 И Идентификатор: — группы (GID) 253, 265, 599 — первичной группы 329, 335 — пользователя (UID) 23, 253, 265, 296, 599 — процесса (PID) 123, 187, 242, 315, 598, 602, 606, 612, 626, 667 — родительского процесса (PPID) 612 — файловой системы 271 Имя файла 26, 314, 324 (дефис) 204 — дополнение 93, 134, 140, 141, 182, 216 — расширение 29, 70 — с запятой 27 — с метасимволами 27, 127, 223, 715 — с непечатаемыми символами 240 — с пробелами 240 — с символом подчеркивания 27 — с точкой 27, 28 — соответствующее символической ссылке 282 — строчные и прописные буквы 27 — уникальность 241 Индексный дескриптор 232, 234, 244, 253, 257, 261, 262, 268, 271, 298, 345, 348, 356, 366 — виртуальный 271 — время изменения 317 — и удаление файла 355 — каталога 325 — квота 373 — номер 34, 275, 278, 335, 353, 355 — описание 34 — счетчик ссылок 278 — чтение 323 Интерпретатор команд (см. Shell) Кавычки: — в Bourne shell 122, 124-126 — в С shell 124-126 Канал 18, 195, 711 — буферизация данных 202 Каталог: — выделение имени из путевого имени файла 242 — для хранения программ редактора Emacs 69 — доступ 38 — извещение об изменении 319 — индексный дескриптор 212, 325, 353 — корневой 32, 33 — начальный 32, 88, 599 — организация 68 — поиск 219 — невыполняемых команд 116 — определение 31 — персональный 69 — подкаталоги 32 — поиск 218 — поиск двойников 244 — полное путевое имя 115, 477 — права доступа 325, 334 — резервное копирование 307 — родительский 33, 214 — смена 213, 214, 216, 22] — содержимое 274 — создание 71, 356 — сравнение структуры 245 — ссылки 278, 281, 751 — стек 214 — текущий 27, 33, 200, 210, 211, 220, 257, 287, 289, 353, 366, 599 — определение 212 — удаление 355, 356 — упорядочение 371, 372 — шаблоны для поиска 230 — /tmp 314, 317 — /usr/tmp 314, 317 Код завершения 50, 59, 94, 168, 352, 431, 526, 710, 715, 718, 727, 729, 739, 757, 759, 760, 762, 765, 776 — проверка 716 — установка 719 Кодировка: — ASCII 568, 569 — EBCDIC 569, 570 Команда: — внешняя 23,24, 96, 104 — встроенная 23, 89, 96, 598, 599, 621, 775 — выполнение в фоновом режиме 191 — выполнение во временно измененной среде 94 — группирование 200, 201 — дополнение в редакторе Emacs 503 — досрочный ввод 134, 150 — интерпретация 109, III — код завершения 715, 719 — многострочная 143 — невыполняемая 116 — повторное выполнение 141, 157, 177 — подстановка результатов выполнения 122, 128, 134, 142, 143, 145, 358, 733, 765, 788 — вложенная 764 890
последовательность поиска 49, 58, 61, 64, 88-90, 115, 116, 681, 712, 714, 737, 858, 862 псевдоним 48,49, 51, 116, 134, 161-163, 165, 213, 216 сигнал прерывания 719 таблица поиска 711 уникальность имени 730 alias 54, 161-168 bg 40, 187, 189, 190, 192,666 builtin 119 cd 33, 65, 79, 88, 93, 99, 103, 119, 120, 201, 209, 211, 213, 217, 219, 221, 300, 343, 470, 59B-600, 731, 749, 775, 846 — опция - (дефис) 215 check (SCCS) 311 ci (RCS) 311, 793 со (RCS) 311, 313, 415 command 119 create (SCCS) 310 delete (пакет delete) 350 delta (SCCS) 310 echo 68, 78, B7, 96, 99, 113, 115, 117-119, 123, 125, 133, 136, 147, 156, 164, 173, 177, 220, 227, 230, 234, 240, 252, 260, 292, 430, 444, 515, 599, 656, 683, 687, 709, 718, 720, 727, 730, 733, 752, 764, 765, 769, 771, 772, 831 — выделение частей строк 763 — опция -е 114, 777, 77В — опция -п ИЗ, 710, 778 — переносимая 777 — получение спецсимволов 768 edit (SCCS) '. 310 enable: — опция -а 120 — опция -п 120 eval 76, 113, 118, 167, 169, 593, 722, 746, 747, 764, 767, 772 ' exec 23, 24, 60, 66, 120, 236, 344, 385, 386, 597, 604, 732, 735, 737, 754 exit 50, 53, 54, 65, 89, 94, 166, 167, IB0, 203, 342, 344, 601, 717, 719, 720, 757, 785, 831 export 320, 622, 737 expunge (пакет delete) 350, 842 fc 183, 184 — опция -е 184 — опция -1 184 fg 40, 187-192 get (SCCS) 310 getopts 723, 725, 726 jobs 40, 120, IB7, 1B8, 192, 613, 666 kill 104, 187, 190, 192, 601, 606, 60В-6Ю, 625, 738 — опция -1 606, 667, 720 — опция -9 606, 607, 611, 667 limit 363 limit filesize 363 Isdel (пакет delete) 350, 842 — newgrp 336, 338 — purge (пакет delete) 842 — pwd 33, 95, 96, 212, 220, 282, 334 — rcsdifr (RCS) 313 — rcsmerge (RCS) 312 — read 66, 143, 261, 721, 733, 747, 749, 756, 757, 765, 779 — rlog (RCS).. 312, 313 — set 54, 61, 65, 72, 76, 77, 80, 87, 90, 92, 97, 115, 199, 218, 315, 344, 576, 613, 710, 727, 728, 746, 767, 785, 797 — выделение частей строк 763 — опция notify 192 — опция +е 774 — опция +v 728 — опция -е 773 — опция -о 66 — опция -и 80 — опция -v 127, 633, 728 — опция -х 80, 127, 633, 770 — setenv 86, 87, 90, 622 — shift 710, 723, 725, 746, 787 — trap 48, 64, 104, 606, 607, 613, 719, 732, 738, 760 — аргументы 738 — номера сигналов 720 — type 72, 120, 730 — опция -all 72 — ulimit: — опция -с 363 — опция -f 363 — umask 71, 180, 288, 327, 329, 332, 334, 600, 768, 795, 859, 863 — unalias 121 — undelete (пакет delete) 350, 842 — unedit (SCCS) 311 — unlimit coredumpsize ....363 — unset 87, 92, 127, 673, 713 — unsetenv 87, 505 — wait 612, 716 — whence 72 — who am i 56, 95, 343 Командная строка: — анализ 118 — в Bourne shell 118, 726 — в С shell 781 — аргументы 113, 385, 386, 722-724, 746, 774 — обработка в цикле for 723 — обработка в цикле while 725 — подсчет 723 — интерпретация 16, 112 — в bash 119 — в Bourne shell 112 — в С shell 112, 119, 120 — основные возможности 134 — повторный ввод 135 — приглашение 101, 103, 170, 343 Предметный указатель 891
— редактирование ПО, 134, 170, 181 -г- в bash ПО, 135, 181 — в С shell 181 — в Korn shell 88, 135, 181-183 — в tcsh shell 181 — слишком длинная 134, 151, 152 Компакт-диск: — демонтирование ;...864 — монтирование 854 — форматы 854 — High Sierra 854, 855 — ISO 9660 854, 855 Компилятор: — ее. 140, 869, 877 — опция -с '. 877 — опция -1 877 — опция -1 :....877 — опция -о 877 — gec : 869 Компиляция.... 869 Компоновщик :...8б9 Конвейер........ ...; 17 Копирование: — атрибутов прав доступа....: 338 — дерева каталогов 287, 289 — резервное .291, 298, 346 — выборочное 305 — каталогов , ..... 307 — необходимость 299 — с помощью локального ленточного накопителя 299 — с помощью удаленного ленточного накопителя :.... 303 — ссылок 288 — файлов 274 м Магическое число 826 Массивы: — ассоциативные ...247 — в basli Ill — в Bourne shell 767, 787 — в С shell 52, 59, 104, 111, 138, 767, 778, 787 — в Кош shell 767 — в tcsh shell Ill — argv ..:.. 774, 785 Метасимволы 128, 134, 173, 177. 223, 253 — в именах каталогов 255 — в именах файлов 223 — в путевых именах 227 — в регулярных выражениях 391, 394, 402, 403 — в bash 224 — в Bourne shell .226, 230 — в С shell ......,:....224, 226, 230 — в Кот shell ......: 224 — в tcsh shell 224 — для скрытых файлов 226 — и дополнение имен файлов 141 — и создание файлов 136 — и удаление файлов 345, 352 — основные, таблица 224 Многозадачность 186 Обратные кавычки: — вложение 147 — подстановка результатов выполнения команды 142, 143, 145 Оператор: — подстановки параметров (Bourne shell) 139 — редактирования строк: - в bash 139, 745 - в С shell 97, 137, 139 - в Кот shell 139, 745 — break '. 739, 757 — breaksw 788 — case 21, 56, 99, 235, 236, 333, 380, 555, 562, 572, 709, 714, 723-725, 733, 745, 754, 757, 766, 771, 773, 792 - шаблоны 715 — do 143, 724 — done 143, 718, 724, 757 — else 167, 716, 739 — endsw 788 — esac 724, 771 — fi 716, 771 — if 59, 100, 167, 716, 729, 739, 754, 771, 773, 778, 783-785, 787, 788 — in 143 — switch 56, 58, 99, 217, .778, 788 -.then..... 716, 739 П Пакетное редактирование 509 — в редакторе ed 515 — в редакторе ex 515 — в редакторе sed 514, 518 — ограничения на размер файла 516 — редакторы 509 Пароль: — группы.... .......336 — автоматизация ввода.... 159 Переадресация ввода-вывода 42, 94, 194, 377, 422 — безопасная 199 — в bash 195, 199 — в Bourne shell 115, 121, 195, 197, 202, 751, 757, 771, 780 — в С shell 115, 195-199, 202, 751, 780 — в Кот shell 121, 195, 199 — канал : ,х..... 195 №
Переменная: - интерпретатора shell 86, 92 - подстановка 19 - трассировка 127 - cdable_vars 220 - cdpatlf. 48, 54, 58, 93. 209, 213, 218, 220, 787 - complete 93, 140 - cwd 93, 96, ЮЗ, 220, 335 - echo 93, 127, 633 - fignore 93, 140 - filec 93, 140 - hardpaths 93, 221 - histchars 93, 184, 185 - history 92, 93 - ignoreeof 53, 65, 66, 94, 167, 180, 666 - loginshell 53 - mail 94, 318, 319, 787 - nobeep 140 - noclobber 92, 94, 199, 348 - noglob 76, 77, 94, 673 - nolinks 221 - rionomatch 94 - notify 94, 191 - path 80, 90, 94, 115, 787 - prompt 48, 54, 55, 88, 94, 95, 97, 632 - promptpid 105 - rmstar - 346 - savediis 215 - savehist 93, 179, 180 - shlvl 101 - status 94, 716 - tf. 795 - lime 94, 615 - user 94 - verbose 93, 127, 633 Переменная среды 48, 49* 86, 92 - передача процессу 87 - стандартная 88 - AT , 55 - CBLKWID 560 - CDPATH , 213 - CHRC_READ 55 - COLUMNS 572, 672 - DISPLAY 57, 89 - EDITOR 88, .344, 805 - ENV 48, 52, 56, 88, 101 - ENV_SET 53 - EXINU 88, 94, 162, 456, 472, 477 - EXSTAT 465 - FCEDIT 184 - FIGNORE 140 - HISTFILE 179 - HISTFILESIZE 179 - HISTSIZE, . ., 170 - HOME 52, 64, 66, 88, 113, 214, 219, 343, 599, 782 - HZ 828 - IFS 73, 106, 238, 576, 610, 725, 728, 763 - INSTALLDIR 867 - INSTALLSHAREDIR 867 - LESS 379 - LINES 672 - LOGDIR 52, 64, 66, 88, 214, 219, 343 - LOGNAME 88 - LPDEST. 682 - MAIL 320 - MAILCHECK 318, 320 - MAILPATH 320 - MANPATH 88, 823 - PAGER 88, 782 - PATH 58, 69, 72, 86, 88-90, 115, 119, 139, 162, 214, 238, 343, 599, 601, 641, 737, 782, 797, 817, 819 - PS1 88, 94, 95, 97, 100, 104 - PS2 : 88, 94, 143 - PRINTER 88, 681, 682 - PROMPT_COMMAND 106 - PWD 88, 97, 220, 335, 505 - SH_EXECD 61 - SHAREPREFIX 867 - SHELL ;88 - SHLVL Ю1 - SOURCEDIR 867 - TERM 56, 75, 77, 79, 83, 85, 86, 88, 89, 100, 340, 632, 659, 668, 672, 677, 694, 714, 782 - TERMCAP 76, 77, 83, 88, 668 - TERMINFO : 83 - TF 315 - TMPDIR 858 - TZ 89-91, 600 - USER 88, 94. 782 - VISUAL 88, 745 - WORDLIST 438, 442 Перечень введенных команд 95, 101, 109, 112, 126, 134, 137, 141, 163, 170, 171, 832 - в bash -ПО, 170, 171, 173, 179, 181, 183-185 - в Bourne shell 185 - в С shell 93, 170, 171, 173, 179-181, 183, 184, 419, 499, 832, 849 - в Кош shell 170, 171, 179, 181-184, 499 - передача другому интерпретатору 181 - просмотр 179 - редактирование 183 - сохранение 179 Печать. 678 - в Berkeley-снстемах 680 - в System V 680 - выбор принтера 682 - длинных строк 685 - и символические ссылки 683 - имени файла в заголовке 686 Предмегиь/й указатель 893
— каталог буферизации 683 — крупными буквами. 687 — на терминальном принтере 683 — основные команды 679 — отступы 685 — система буферизации 678 — управление принтером 681 — формат PostScript 679, 698, 699 Подкачка 827 Подстановка параметров 744 Поиск: — алгоритм Бойера-Мура 414 — без учета регистра 419 — в редакторах ех и vi 457, 458 — в редакторе Emacs 504 — в сетевых файловых системах 273 — в файлах 408 — в RCS-файлах 414 — закрывающих скобок 446 — команд 115, 238 — комбинированный 418 — многострочный 415 — по нескольким шаблонами 412 — по шаблону 256, 391, 404, 406, 408 — путевых имен 268 — с учетом позиций символов 420 — символических ссылок 263 — несвязанных 250 — слов 398, 410 — в двоичных файлах 420 — повторяющихся 398 — удвоенных 445 — ссылок на файл 271 — текста 409, 410 — несовпадающего 409 — файлов 218, 271, 273 — по временным характеристикам 259 — по заданному слову 270 — по имени владельца 265 — по имени группы пользователей 265 — по правам доступа 264 — по размеру 263 — по содержимому 269 — по типу 263 — старых 256 — текстовых 248 — ускоренный 267 Последовательность поиска команд 49, 58, 61, 64, 88-90, 115, 116, 681, 712, 714, 737, 858, 862 — изменение 116 — установка 115 Последовательный порт 646 — контроль четности 647 — настройка 645 — скорость в битах. — скорость в бодах. — скорость exta — скорость extb. 647 647 647 647 установки 654 tty 67, 598, 604, 779, 827, 830 Поток — ввода (stdin) 194, 204, 304, 733, 750, 756, 758, 779 — вывода (stdout) 121, 194, 196, 197, 201, 202, 289, 516, 672, 750, 751, 760, 766, 770, 779, 780 — ошибок (stderr) 121, 194, 196, 197, 516, 750, 751, 757, 766, 779, 780 Права доступа 35, 222, 262, 264 — атрибуты 325, 326 — бит поиска 39 — бит SGID 37, 39, 265, 329, 336, 371 — бит SUID 37, 39, 264, 337, 370, 371, 593 — биты режима 36 — для владельца 326 — для группы 326, 328, 329, 335, 336, 858 — изменение 330, 711 — к каталогам , 38 — к сценариям shell 340 — к файлу 360 — в BSD UNIX 341 — копирование .338 — краткое руководство 325 — манипулирование 337 — символьное представление 330 — sticky-бит 37, 39, 314, 330, 331, 334 Предметный указатель 821 — построение 816 Препроцессор 869 Приоритет: — задания 625 — процесса 622, 625 — реального времени 623 — фактор уступчивости 623 Проверка правописания 420, 437, 439 — интерактивная 438 Программа: — время выполнения: — полное 614, 618 — пользовательское 614, 618 — системное 614, 618 — среднее 617 — запуск в интерактивном режиме 187 — запуск в фоновом режиме 186 — приостановка выполнения 187 — хронометрирования 614 — ab 72 — agrep 408, 412, 414, 418, 844 — опция -d 413 — apropos 815, 817-819, 821, 846 — имитация 815 894
- аг 821 - at 51, 54, 55, 102, 245, 268, 328, 370, 613, 627, 630, 631, 633, 634, 636, 637, 795, 821, 847 — выбор командного интерпретатора 632 — опция -с 632 — опция -1 633, 634, 636 — опция -г 634, 636 — опция -s 632, 635 — повторный запуск заданий '....635 - atq 633-635 — опция -с 635 — опция -п 635 ■ atrm 634, 635 - aWf. 693, 839 - awk 20, 182, 203, 251, 267, 269, 384, 394, 398, *-i a 416, 417, 420, 447, 509, 544, 559, 574, Л15 576, 610, 690, 709, 721, 734, 736, 763, 791, 807, 818, 821, 838, 839, 844, 848 — ассоциативные массивы 247 — версии 526 — выравнивание колонок 577 — вычисление даты 243 — и язык Perl 589, 591, 594 — изменение приоритета 624, 625 — имитация команды grep 408 — использование кавычек 124 — код завершения 523 — команда printf 563, 578 — команды, список 522 — комбинированный поиск 418 — краткий справочник 518 — массивы 521, 767 — операторы 521 — опция -f. 518, 735 — опция -v 519 — ошибки 771 — ошибки в сценарии 825 —' переменная FS 525 — переменная OFS 524 — переменные 521 — подсчет количества повторений слов 445 — поиск текста 411 — получение итога по столбцу 812 — процедуры 520 — разбиение текста на колонки 573 — разделители полей 245 — регулярные выражения 406 — синтаксис командной строки 518 — системные переменные 520 — создание базы данных фамилий и адресов.... 800 — создание отступов в тексте 562 — сортировка строк по длине 587 — форматирование колонок 572 — центрирование строк 563 — шаблоны 519 ■ backup 346 - banner , 578, 687 — опция -w 687 - baseline 138 - basename 97, 117, 730, 733, 746, 748, 850 - batch 613, 627, 630, 633 - be 808, 810 — команда ibase 809, 810 — команда obase 809, 810 — команда scale 808 — опция -1 808, 810 — преобразование в двоичный формат 809 — преобразование в шестнадцатеричньгй формат... 809 — функция cosine 810 — функция sine 810 - bdilT 427 - bsdtar 840, 869 - bsplit 564, 840 - cal 115, 796-798, 840 - calen 797, 840 - calendar 411, 637, 793-795 - cancel 680 - cat 16, 33, 74, 111, 114, 164, 167, 168, 196, 204, 206, 251, 292, 304, 308, 350, 361, 363, 377, 378, 385, 430, 620, 652, 654, 655, 668, 671, 693, 709, 713, 717, 736, 747, 793, 807, 817, 831 — опция -е 240, 380, 395 — опция -п 390, 685 — опция -s 246, 384, 385 — опция -1 380, 382, 582 — опция -v 175, 380-382, 395, 420, 582, 663, 676, 694, 831 — проверка размеров экрана 674 - catman 816 - cbw 339 - cdromd 856 - chgrp 35, 255, 329, 338, 843 - chmod 69, 115, 167, 232, 234, 255, 262, 326, 330. 332-334, 336, 338, 711, 713, 843, 858 — операторы 331 — опция -R 331, 332 - chown 35, 341, 843 - chsh 60 - clean 358 - clear 63, 75, 340, 668 - clri 279, 353 - emp 431, 842 - col 558, 816 — опция -b 694, 818 — опция -x 694 - colcrt 694 - colrm 272, 560, 571, 711, 763, 818 - comm 59, 147-149, 246, 431, 575 - comp 43 - compress 296, 339, 363, 364, 380, 871 - cp 44, 136, 255, 322, 323, 333, 362, 374, 429, 715, 717, 759, 843 Предметный указатель 895
— опция -b ■■ 322 — опция -i:...: 283, 322 — опция -р 288, 341 — опция -г 287, 289 — опция -г 374 cpio 291, 297, 298, 305, 392 — опция -d 297 — опция -i 297 — опция -о 297 — опция -р 297 — опция -v 297 cpmod 318, 332, 338, 841 стоп 104, 160, 162, 245, 268, 357, 370, 412, 608, 633, 635, 637, 638, 640, 840 — использование стандартного ввода 640 crontab 630, 640, 641 — опция -е 328, 640, 641 — опция -1 640 — опция -г 640 crypt 339 csplit ..394, 443, 557, 565, 842, 852 — опция -f. 566 — опция -к 567 — опция -s 566 cut 146, 156, 268, 272, 390, 479, 557, 560, 570, 571, 574, 576, 577, 686, 711, 728, 763, 807, 818, 837, 842, 852 — опция -с 570 — опция -d 570, 574 — опция -Г .570 date 89, 91, 105, 145, 242, 243, 315, 324, 614, 636, 639, 797, 834, 850 dbx 370, 394, 404 dc 808 — команда р 808 dd ; 298, 303, 339, 557, 561, 565, 569, 570, 780, 843 — опция bs= 561 — опция cbs= 570 — опция conv= 569, 570 — опция ibs= 561 — опция if= : 561, 569 — опция obs= 561 — опция оГ= 569 — опция skip-... 561 del 346 delete 349, 350, 842 — команда delete 350 — команда expunge 350, 842 — команда lsdel 350, 842 — команда purge 350, 842 — команда undelete 350, 842 deroft" 445 — опция -w 440, 447 df. 271, 366, 373, 843 — опция -i 366 — опция -1 366 — опция -t 366 - dfspace 366 - diff 59, 148, 204, 246, 255, 283, 422, 424, 426, 428, 431, 433, 509, 511, 514, 516, 715, 839, 840, 842, 847 — опция -с 153, 425, 517 — опция -е 247, 422, 429, 515 — "опция -h 427 — опция -г 245 — опция -t 431 — проблемы 431 - difD 423, 424, 427, 842 — опция -е 424 - diflmk 436 - dir 241 - dircmp 245, 246 — опция -s 246 - dimame 242, 733, 748, 850 - dire 97, 103, 215, 221, 787 — опция -с 215 - display..: 832 - ditroft". 657, 690, 692, 693 - doctor 508 - du 255, 366, 444, 843 — опция -а 367 — опция -s 367 - dump „..'. 37, 310 - ediff 428, 842 - egrep 268-270, 306, 394, 398, 402, 406, 408, 410-412, 414, 415, 418, 609 610, 793, 794, 816, 842, 844 — опция -f 411, 412, 794 — опция -v 273 — опция -i .270, 419, 791 — GNU-версия 414 - eject 856 - elm 43 - email 313 - end 142 - enscript 699 - env 57, 87, 92, 94, 102, 218/315, 344, 632, 850 - eqn 844 - expand 363, 420, 431, 563, 573, 652, 686, 843, 852 - Expect 158-160, 347, 843 - expr 123, 242, 394, 404, 443, 572, 733, 741, 762, 764, 808, 811, 850 — арифметические операторы 760 — выделение частей строк 762 — код завершения 760, 762 — краткий справочник 760 — логические операторы 761 — операторы сравнения 760 - false 716, 850 - fgrep 266, 269, 408, 411, 414, 843 — опция -f 412 896
— omr.<;. „358 — onuiL л 412 file 247, 248, 370, 383 find 65, 68, 83, 149, 151, 152, 154, 223, 231, 245, 247, 251, 253, 255, 257, 262, 267, 269-271, 273, 297, 298, 306, 327, 357, 359, 369-371, 392, 410, 417, 639, 748, 759, 765, 818, 840, 853 — быстрая 265, 267 — оператор {} 260, 265 — опция ! 254, 258 — опция -а 254 — опция -atime 254, 257, 259, 317, 357 — опция -cpio 358 — опция -ctime 254, 257, 317 — опция -exec..254, 258, 260-262, 265, 331, 332, 359 — опция -fttype 273 — опция -group 253, 264 — опция -inum 253, 261, 355 — опция -links 257, 290 — опция -Is 256, 263, 272 — опция -maxdepth 272 — опция -ratime 254, 256, 258, 260, 295 — опция -пате 253, 256, 257, 262, 273 — опция -newer 156, 254, 259, 305, 360 — опция -nogroup 265 — опция -nouser 265 — опция -о 254, 258,. 272 — опция -ок 254, 261, 346, 357 — опция -регт 253, 264 — опция -print 154, 254, 256, 257, 260, 263, 272, 273, 359 — опция -prune 156, 271, 273, 355 — опция -size 253, 263 — опция -type 253, 262-263, 290 — опция -user ;. 253, 264 — опция -xdev 273 — GNU-версия 843 fmt 151, 476, 479, 487, 498, 557, 558, 685, 844, 849 — опция -1 558 — опция -р 558, 559 — опция -s 558 — форматирование строк комментариев 559 fold 205, 381, 386, 557, 677, 685 fortune 48, 64 fsck 26, 353, 356 ftp 43, 47, 62, 150, 160, 294, 865 ftpd 62 gawk 196, 204, 415, 446, 526, 586, 844, 848, 861 — и язык Perl 589 — команда print 586 — команды, список 522 — операторы 521 — системные переменные 520 getopt 710, 723, 725, 726, 844, 847 - getty „ .-.. 598 - glimpseindex 412 - gnroff 305, 693, 822, 844 - grabchars 66, 721, 733, 765, 844 — код завершения 765 — опция -b 766 — опция -d 766 — опция -t 766 - grep 16, 68, 72, 145, 151, 155, 178, 182, 197, 205, 224, 248, 256, 260, 266, 269, 270 302, 308, 315, 335, 364, 388, 392-395, 398, 401, 406, 408-411, 413^121, 433, 437, 470, 510, 543, 584, 628, 679, 690, 709, 715, 718, 737, 749, 752, 761, 762, 780, 796, 815, 816, 818, 830, 840, 844, 853 — опция -с 228, 409, 811 — опция -h 18 — опция -i 270, 409 — опция -1 146, 153, 156, 227, 409, 418 — опция -s _ 59 — опция -v 358, 409 — опция -w 409, 410 - groff 688, 692, 693, 822, 844 - groups 336, 338, 850 - gsed : 547 - gunzip ....203, 295, 296, 364, 845, 871 - gzcat 203, 296, 364, 367, 380, 723, 724 - grip 29, 268, 294, 296, 317, 363-365, 368, 380, 837, 845, 865, 867 — опция -f 364 — опция -v .370 - head 205, 237, 250, 313, 389, 602, 671, 686, 834, 845 - history 170, 174 — опция -h 180, 181 — опция -г 180 — опция -w 180, 181 - hostname ,..,....819 - id 343, 344, 850 - inc 43 - indent 466 - index 805, 845 — опция -f 807 — с фильтром 807 - inetd 160 - mil 598, 607,612 - install 843 - iostat 627 - ipl 791, 813, 845 - ispeU 437, 438, 441, 845 — команда I 442 — опция -а 442 '— опция -d 442 — опция -1 439 — опция -р 438, 442 — опция -x 439 — таблица команд 438 Предметный указатель 29 9-171 897
• join 574, 577 — опция -t 575 ■ jot 732, 741, 830, 845 — опция -b 743 — опция -с 743 — опция -р 742 — опция -г 743 — опция -s 743 — опция -w 743 — получение спецсимволов 768 lastcomm 611, 620, 811 Id 370, 825 • leave 630, 795 less 84, 88, 197, 378-380, 404, 694, 819, 845 — команда h 379 — опция -с 378 — опция -s 246 Iesskey 379 lex 394, 404, 447 line 751 In 323, 362, 843 — опция -s 279, 282 Indir 282, 846 login 47, 49, 598 logout 65, 89, 94, 344 look 245, 420, 439, 846 — опция -d 440 — опция -f. 440 Ip 179, 434, 652, 679, 680, 699, 798 — опция -d 680, 682 — опция -u 680 Ipc 681 — команда exit 681 — команда help 682 — команда restart 681 — команда status 681, 682 lpq 608, 680, 681, 832 — опция -Р 681 lpr 38, 88, 136, 144, 150-152, 321, 679-681, 685, 699, 718, 798 — опция -Р 681, 682 — опция -s 683 — ошибки 827 lprm 680 Ipstat 680 Is 23, 28, 40, 68, 115, 120, 134, 152, 161, 173, 211, 223, 231, 233, 234, 236, 239-241, 255, 272, 278, 285, 286, 321, 334, 353, 355, 358, 370, 382, 410, 432, 574, 615, 749, 825, 826, 842, 843, 846, 851 — вывод скрытых файлов 238 — вьгаод содержимого подкаталогов 234 — опция -а 239, 355 — опция -А 229, 230, 238, 278, 351, 443 — опция -Ь 241, 353, 354 — опция -с 232, 233, 239, 317 — опция -d 60, 173, 230, 236, 358 — опция -F 230, 235, 236, 239, 275, 280, 373 — опция -g 236, 329, 338 — опция -i 35, 261, 275, 278, 279, 355, 820 — опция -1 35, 80, 155, 164, 229, 232, 236, 238, 239, 241, 243, 247, 251, 261, 267, 268, 278-280, 298, 301, 305, 317, 319, 326, 328, 337, 360, 363, 373, 419, 443, 571, 686 — опция -т 236 — опция -q 240, 353, 354, 355 — опция -R 83, 233, 234 — опция -s 263, 373, 795 — опция -t 59, 231, 233, 298, 305 — опция -и 232, 233, 317 — опция -х 233 - mail 43, 147, 192, 194, 196, 199, 470, 631, 637, 639, 672, 793, 839 — опция -s 637 — mailshar 850 — mailx 43, 672 - make 305, 320, 339, 433, 435, 464, 624, 846, 869, 874, 877 — опция -n 876 — опция install 876 — ошибки 826 — GNU-версия 869 — makealias 168, 250 — makedev 271 - man 88, 303, 357, 384, 609, 696, 814, 816, 819, 822 — опция -к 301, 815 — опция -М 823 — опция -s 815 — manindex 846 — map 72 - mark 209, 220 - mkdir 71, 265, 356, 843 — опция -т 71 — опция -p 71, 869 - mkfifo 843 - mkfile 827 — mknod 843 — more 88, 151, 164, 167, 174, 197, 211, 233, 234, 293, 364, 368, 377-380, 383, 387, 394, 404, 419, 528, 599, 671, 674, 686, 736, 782, 814, 817, 826, 845, 853 — взаимодействие с терминалом 75 — команда b 378 — команда h 378 — команда n 379 — команда v 378 — команда :п 378 — команда :р 378 — недостатки 379 — опция -с 378 — опция -s 246, 384, 822 - mount 271, 854 898
— опция -г 855 • mt 301 • multi 362 ■ mush 43 ■ mv 136, 240, 275, 283, 286, 322, 323, 328, 350, 361, 362, 407, 843 — опция -b 322 — опция -f 332 — опция -i 283, 322 nawk 526, 759, 800, 825, 849 — и язык Perl 589 — команды, список 522 — краткий справочник 518 — операторы 521 — переменная FNR 523 — переменная NF 523 — переменная NR 523 — переменная RLENGTH 524 — переменная RSTART 524 — системные переменные 520 ncheck 271 netstat 627 newaliases 361 newer 250 news 839 nfsstat 627 nice 603, 621, 622, 625, 626, 850 — интерпретатора С shell (BSD) 623, 624 — интерпретатора С shell (System V) 624 — самостоятельная версия (BSD) 624 — самостоятельная версия (System V) 625 nl 390 nm 417 nohup 850 nroff 200, 383, 409, 433, 438, 467, 558, 613, 679, 690, 693-695, 697, 807, 818, 839, 844, 861 — взаимодействие с терминалом 75 — макрос -man 822, 823 — макросы 691 — опция -е 822 — отчет о времени выполнения 615 od: — опция -с 274, 354, 381, 655 — опция -s 421 pack 296, 339 passwd 158, 159, 815, 843 paste 557, 573, 574, 837, 842, 852 — опция -d 574 — опция -s 574 patch 426, 509, 514, 516, 837, 847, 868, 874 — опция -d 517 — опция -R 517 pathchk 850 peal 798, 848, 870, 873, 874, 877 — опция -h 799 — опция -m 800 — опция -n 800 — опция -w 798 — perl 404, 509, 516, 544, 565, 573, 588, 591, 735, 807, 838, 848, 868, 874 — имитация команды grep 408 — комбинированный поиск 418 — опция -0 .418 — поиск текста 411 — pg 75, 197, 233, 379, 380, 394, 404, 686, 719, 724, 770, 845 — pic 844 — pico 184 — Pine 43 — popd 93, 103, 119, 158, 214, 221, 787 — ppc 794 — pr 151, 152, 155, 233, 245, 321, 571, 572, 652, 672, 684-686, 711, 726, 818 — опция -d 572 — опция -е 431, 652, 686 — опция -F 685 — опция -h 313, 686 — опция -1 573 — опция -m 572 — опция -п 390, 573, 685 — опция -о 686 — опция -s 573 — опция -t 390, 563, 572, 573, 652 — printenv 57, 87, 92, 218, 315, 632, 850 — printf. 113, 114, 584, 850, 851 — priocntl 623 — ps 25, 61, 188, 190, 409, 597, 601, 604, 607, 609-611, 622, 627, 670, 853 — опция -а 26, 602, 610 — опция -е 26, 603 — опция -f 603 — опция -g 610 — опция -1 26, 602, 626 — опция -и 26, 602, 667 — опция -х 26, 602, 603, 667, 796 — перечень полей вывода 602 — psc 812 — psroff 693 — psselect 699, 700 — опция -е 700 — опция -о 700 — опция -р, 700 — опция -г 700 — pstat 627 — pstext 699, 798, 848 — список опций 699 — ptroff. 470 — pushd 93, 103, 119, 214, 216, 220, 221, 787 — pwck 80 — qsubst 518, 848 Предметный указатель 29* 899
— опция -noask 518 qterm 58, 77, 848, 860, 872, 875 — опция +usrtab 78 quote 168 rep 30, 44, 155 — опция -г 287 refer 844 rehash 69, 711 remsync 850 ren 284, 849 — опция -a 285 — опция -d 285 — опция -к 285 renice 190, 623, 625 rep 832 repeat 157, 215 reset 84 resize 673 riogiri 44, 57, 67, 303, 673 rm.. 38, 65, 110, 115, 120, 145, 154, 163, 173, 223, 226, 227, 240, 255, 278, 280, 293, 299, 311, 312, 322, 345, 346, 350, 354, 355, 358, 361, 740, 775, 843 — опция -f 332, 346, 351, 720 — опция -i 322, 346-348, 351, 354, 358, 842 — опция -г ...190, 349, 354, 356, 365, 826 — проблемы при использовании 345 rmdir 355, 356, 826, 843 m... 516 rogue 75 rot 578, 849 rsh ....44, 52, 53, 65, 96, 102, 204, 290, 303 sadp 627 sar 627 — опция -q. 626 sc 791, 812, 813, 849 screen 66, 192, 193, 850 — опция -г 67 Scribe .689, 690 script 202, 569, 621, 676, 830, 831, 850 sdiff 426, 427, 842 — опция -о 427 — опция -s 426 — опция -w 426 send 159 send mail 372 shar 291-294, 298, 850, 873 show 43 shutdown 26 sleep 40, 63, 150, 613, 630, 635, 654, 738, 769, 795, 850 sis 243, 251, 268, 851 — опция -1 251 — опция -и 251 smiley 835, 851 — опция -1 835 - sort 148, 316, 466, 574, 577, 579, 582, 584, 585, 587, 591, 738, 819 — алфавитная сортировка 583 — опция -b 581, 583, 584 — опция -d 585 — опция -f 583, 585 — опция -М 585 — опция -г 585 — опция -t 581 — опция -и 18, 146, 157, 440, 447, 584, 711 — правила интерпретации пробельных символов 582 — числовая сортировка 583 - source 58, 165, 167, 183, 221, 600, 731, 780, 841, 849, 850 — опция -h 180, 181 - spell 437-440, 845 — опция -b 440 — опция -v 441 — опция -х 441 - spellprog 440 - split 557, 563, 565, 748, 840 — варианты 564 - sqtroff 692 - stat .....35, 251, 271, 323, 851 - stop 190, 192 - strings 249, 382, 420 — опция - (дефис) 421 - strip 370, 371 - stty 44, 48, 80, 81, 84, 605, 645, 646, 648, 650, 654, 663, 665, 669, 850 — изменение функций клавиш 82 — определение установок терминала 651 . - опция -а 82, 135, 670, 672 — опция -echo 760 — опция -g 572, 576, 669 — опция -icanon 655 — опция -tabs 652-654 — опция cbreak „ 655 — опция erase 56, 81, 495 — опция everything 82, 669 — опция icanon 655 — опция old 653 — опция rprint 82 — опция size 672 — опция tabs 654 — опция tilde 654 — опция tostop 188, 191, 192,654 — опция werase 82 - su 50, 54, 61, 79, 102, 340-343, 601 — опция - (дефис) 344 — опция -е 344 — опция -f 79, 344 900
— опция -m :■■■:■■" 344 — опция -р 344 suspend 192, 342, 601 swapon 827 sync 26 syslogd 608 tabs 80 tail 218, 386-388 — опция +n 387 — опция -b 387 — опция -с 387 — опция -f 199, 202, 203, 387, 388, 770, 853 — опция -1 387 — опция -п 387 — опция -г 313, 387, 389, 578 talk 44, 827 tar 29, 236, 255, 291, 297, 298 339, 364, 572, 636, 657, 765, 840, 874 — опция -С 307, 309 — опция -1 295, 306 — опция —absolute-names 295 — опция —after-date 295 — опция —keep-old-files 295 — опция —old-archive 295 — опция В 289, 303 — опция b 309 — опция с 294, 296, 300-302, 308, 309 — опция F 306 — огптия f 294, 296, 309, 871 — опция FF 306 — опция h 277 — опция 1 365 — опция о 295, 296, 872 — опция t 296, 300-302, 308, 309, 365 — опция v 289, 296, 300, 301, 365, 871 — опция X 306, 309 — опция х 295, 300, 871 — опция Z 295 — порядок аргументов 309 — синтаксис 300 — сообщения 296 — GNU-версия 295, 297, 298, 302, 303, 305, 309, 851 tbl 530, 535, 558, 807, 844 tc 657, 851 tcap 81, 85, 572,. 656, 6*7, 851 tee 201, 202, 441, 676, 770, 850, 852 — опция -а 202 telnet 44, 49, 158, 673, 843 — потеря связи 666 test 222, 273, 444, 639, 710, 713, 717, 723, 725, 729, 745, 757, 766, 831, 850 — опция -а 766 — опция -о 766 — опция -х .710 — ошибки 772, 773 - ТЕХ 416, 439, 623, 679, 688-690 — препроцессоры 691 - through 156 - tic 83, 659 - time 190, 614, 615, 617, 735, 849 — /bin/time 614, 618 - timex 615 - tip 159 - touch 60, 136, 259, 317, 321, 339, 349, 435, 776, 843 - tpipe 202, 852 - tput 81, 84, 85, 101, 572, 645, 656, 657, 665, 669, 851, 852 — опция init 658 - tr 19, 196, 227, 230, 244, 445, 447, 467, 479, 535, 557, 567, 586, 591, 733, 831 — версия для Berkeley UNIX 568 — версия для System V... 568 — огшия -с... 447 — опция -d 444, 568, 655 — опция -s 447, 568 — получение спецсимволов 768 - troff. 18, 29, 133, 228, 387, 402, 416, 433, 439, 446, 447, 475, 490, 533. 546, 623; 679, 683, 688-690, 693, 698, 822, 844 — макрос .те „....436 — макросы 691, 696 — опция -t 827 — ошибки „..827 — предварительная обработка ввода .....697 — препроцессор eqn 688, 689 — препроцессор pic 688, 689 — препроцессор tbl 688 — список препроцессоров 692 - true 90, 155, 716, 739, 850 - tset 44, 48, 53, 56, 58, 81, 82, 85, 88, 645, 665, 668, 669 — взаимодействие с терминалом 75 — определение типа терминала 75 — опция -т 75 — опция -Q 76, 84 — опция -s 77 - tty 58, 66, 67, 604, 632, 850 - twin 427, 852 — команда j 427 — команда и 427 — команда v 427 - ul 75, 694 — опция -t 694 - umount 864 - uname 850, 856 — опция -n 100, 819 - uncompress 364, 868, 871 Предметный указатель 901
unexpand 363, 487, 653, 852 — опция -a 363 uniq 584 — краткий справочник 575 — опция -с 441, 575 — опция -d 575 — опция -и 575 unshar 292, 293, 850, 868, 872 uptime 49, 621, 626 users 728 uucp 38, 43, 47, 185, 294, 319, 386, 388, 866 uudecode 295, 850, 866 uuencode 171, 294, 850, 866 uulog 43, 386, 419 uuname — опция -1 819 uux 43 vipw 80 vis 832, 852 — опция -d 832 — опция -s 832 vmstat 627 vtree 244, 853 w 604 watch 832 wc 114, 401, 443, 811 — опция -с 443 — опция -1 443, 572, 574, 577 — опция -w ■ 443 whatis 817, 819 whereis 72, 238, 817 — опция -b 818 — опция -m 818 — опция -и 818 whereiz 853 which 55, 72, 90, 113, 115, 238, 819, 826, 853 — опция -а 72, 820 — опция -i 820 who ....49, 74, 102, 146, 344, 426, 571, 711, 718, 728, 749, 830, 850 whoami 343, 344, 826, 850 write 44, 67, 135, 143, 336, 608, 795 xargs 147, 151-153, 227, 255, 260, 263, 265, 267-270, 370, 418 — опция -0 154, 843, 853 — опция -п 153 — опция -р 153 — GNU-версия 853 xcalc 808 xditview 845 xtail 388, 853 xterm 42, 52, 57, 79, 599, 673, 715 — опция +ls 47 — опция -Is 47 xwininfo 675 — yes 204, 347, t>75, 74J, 834, 85U — zcat , 364, 367, 872 Производительность 618, 626 — подсистемы ввода-вывода 627 — подсистемы памяти 627 — центрального процессора 626 Процесс: — группа .599, 606, 626 — зомби 607, 612 — идентификатор (PID) 123, 187, 598, 602, 606, 612, 626, 667, 719, 796 — интерактивный 599 — уничтожение 606 — код завершения 600, 715 — остановка 191 — подпроцесс.... 599, 600 — подстановка 134, 147 — в bash 134, 147 — получение переменных среды 87 — порожденный 94, 162, 163, 165, 598-600, 613, 622, 729, 738, 751, 757 — приоритет 622 — изменение 38, 625 — уменьшение 623 — родительский 599, 612 — статистика выполнения 601 — уничтожение 191, 599, 606, 609-612 — управление 598 — управляющий 599 — фоновый 39, 40, 191, 599, 613 — уничтожение 612 Псевдонимы 48, 49, 51, 116, 134, 161-163, 165, 213, 216 — автоматическая расстановка кавычек 168 — автоматическое определение 163 — вложенные 163 — для стандартных команд 161, 162 — и управляющие конструкции 167 — и функции 168 — имитация в Bourne shell 169 — рекурсивный вызов 166 — с аргументами 163 — со специальными символами 117 — сравнение со сценариями 165 Псевдотерминал (pty) 66, 342, 604, 645, 648, 655, 827, 830, 850 Путевое имя 242, 278, 307, 599, 831 — относительное 52, 90, 209, 281, 289, 308, 323, 638, 748, 877 — поиск .268 — полное 32, 61, 72, 115, 137, 183, 209, 212, 266, 281, 295, 308, 438, 472, 477, 748 — в файлах конфигурации 52 — с метасимволами 227 — создание 33
Регулярные выражения 128, 175, 242, 268, 379, 391, 471, 539, 762, 806 — в программе awk 406 — в редакторе ed 394, 395, 406 — в редакторе Emacs 399, 404 — В редакторе ех 46, 406 — в редакторе sed 139, 394, 395, 398, 401, 406, 407, 529, 532, 534, 696 — в редакторе vi 394, 395, 398 — В языке Peri 592 — в С shell 395 — задание диапазона символов 395 — запоминание шаблонов 398 — метасимволы 391, 394, 402, 403 — интерпретация 392 — исключение символа из набора 396 — краткий справочник 404 — ограничение размера совпадающей строки 402 — повторение символов 396 — проблемы 398 — простые 394, 416 — расширенные 394, 398, 410 — символы обозначения позиции 394 — создание 399 — состав 394 — сравнение с любым символом 395 — сравнение с набором символов 395 — сравнение слов 398 — тестирование 400 — шаблоны замены 405, 407 — шаблоны поиска 405-407 Редактирование: — пакетное 509 — В редакторе ed 515 — в редакторе ех 515 — в редакторе sed 514, 518 — ограничения на размер файла 516 — сжатых файлов 368 Режим: — интерактивный 150, 186, 286, 712 — необработанных данных 646 — неполной обработки данных 647 — обработки данных 646, 648 — отладки 79 — разделения времени 622 — терминала 647 — фоновый 39, 149, 186, 191, 197, 712, 718 — ENQ/ACK 650 — RTS/CTS 650, 651 — XON/XOFF 650 Резервное копирование 298, 346 — выборочное 305 — каталогов 307 — необходимость 299 с помощью локального ленточного накопителя.... 299 с помощью удаленного ленточного накопителя.... 303 Свопинг 627 Сигнал: — В Bourne shell 104, 720, 738, 781 — в С shell 738, 781 — для порожденных процессов 738 — определение 604 — прерывания 710, 719 — распределение 599 — список наиболее распространенных 605 — HUP 607, 612, 613, 667, 738 — INT 606, 607, 781 — INTR 191 — KILL 606, 607 — QUIT 389, 605-607, 613, 720 — SEGV 605 — STOP 190, 606, 611 — TERM 606, 607, 613 — TSTP 191, 605 — WINCH 672 Системные часы 828, 834 — синхронизация 828 Системный вызов: — attach 593 — bind 593 — chroot 302 — connect 593 — creat 372 — exec 23, 24, 60, 66, 120, 236, 344, 385, 386, 597, 604, 732, 735, 737, 754 — fcntl 593 — fopen 374 — fork 24, 357, 593, 597, 611, 612, 619 — fseek :. 374 — getc 593 — ioctls 593 — link 372 — mkdir 279, 356 — open. 349 — pipe 593 — read 304 — rmdir 356 — select 593 — socket 593 — stat 271, 323, 325 — syscall 593 — unlink 349, 354, 356 — write 304 Сообщения об ошибках 825 Сортировка 579 — алфавитная 583 — в обратном порядке 585 Предметный указатель 903
— в порядке следования месяцев 585 — игнорирование пробелов 584 — изменение разделителя полей 581 — как в словарях 585 — не чувствительная к регистру 585 — нескольких строк 585 — по длине ...587 — по фамилиям 587 — проблема пробельных символов 582 — советы 584 — удаление повторяющихся строк 584 — числовая 583 Список свободных блоков 345 Справочная система 814 Среда 599 Ссылка: — жесткая 60, 117, 222, 276, 279, 334, 348, 364, 365, 465, 826, 833 — копирование 288, 290 — определение 277 — отличия от символической 278 — перемещение 281 — на каталог 278, 281, 751 — поиск.... 271 — символическая 60, 211, 220, 222, 239, 263, 276, 281, 282, 319, 361, 683, 826, 833 — изменение объекта 287 — копирование 288, 290 — определение 277 — отличия от жесткой _ 278 — перемещение 281 — поиск 263 — проверка целостности 250 — устаревшая 280 — создание : 279 — счетчик 278 — удаление 279, 361 Стек каталбгов 214, 787 Строка приглашения 101, 170, 343 — в bash 95, 97, 100, МО, 713 — в Bourne shell 22, 99, 105, 713 — в С shell 22, 88, 95-97, 103 — в. Korn shell 97, 713 — в tcsh shell 95, 97, 101 — вторичная 88,94, 124, 142, 143, 151 — выделение символов 100 — настройка 23 — первичная 88, 94 — пустая 103 — установка 54, 95, 96 Строки: — выделение частей 762 — редактирование: — в bash 139, 745 — в С shell 137, 139 — в Korn shell 139, 745 Суперпользователь. 38, 39, 187, 190, 346, 356, 366, 607, 636, 855 Сценарий: — автоматизапия набора номера 158 — код завершения 719 — невыполняемый 69 — отладка 770 — непарные операторы 771 — ошибки при сравнении строк 773 — ошибки при сравнении чисел 772 — преждевременный выход 771 — самораспечатьгааюшийся 735 — самоудаляющийся 735, 740 — создание 710 — .enter.csh 838 — .enter.sh 838 — .exiLcsh 838 — .exit^h 838 — address 791, 848 — addup ...444, 811, 839 — age_files 247, 839 — ascii 829, 839 — behead 557, 560, 839 — bkedit 717, 719, 744, 840 — build 867 — build.pt : 868 — buildhash 442 — c-w 332, 333 — C132 .841 — c2—c8 841 — C80 841 — cal 115, 796, 797 — caljoday 797, 840 — catsaway 718, 840 — center v 557, 563. 840 — cgrep 416, 417, 543, 840 — cgrep.sed 415, 416, 543, 840 — checksed 528, 529, 840 — chess 160 — chmod_edit 332, 840 — chunksort 586, 840 — опция -а 586 — опция -о 586 — cleanup ; 359, 840 — cleanup.sed 698, 841 — Clear 656, 841 — elf 234, 235, 841 — clf2 235, 841 — ClrStatus 841 — els 234-236, 841 — cls2 235, 841 — cols 233, 571, 573, 841 — compresser 317 — Configure _ 516, 592, 874 904
count.it 444, 841 count_types 247, 841 crontab 641, 841 crush 384, 385, 841 cryptdir 160 cvtbase 810, 842 cw 332, 333 ex 332. 333 del 316, 348, 842 dir_path 245, 842 dirtop 321, 842 — опция -с 321 doublespace 384, 385, 842 elookfor 270, 842 findemd 90, 238, 843 findtext :248, 843 flip '. :„.. 389, 844 fmt.sh i 559, 844 formprog 755, 757, 844 ftpfile 128, 844 getmac 548, 696, 844 Graphics 841 head 389, 845 hey 241, 845 hgrep 421, 845 — опция -w 421 lensort 587, 845 If 236, 731, 745, 846 lg 236, 731, 846 II .- .......236, 731, 846 lm 236, 846 locate 265, 267, 818, 843 logerrs 206, 846 lookfor 270, 842, 846 lr 236, 846 ls_today 243, 846 motd.diff 58, 846 munchlist 442 namesort 587, 846 nextday 636, 847 — опция -n 636 nextweekday 636, 795, 847 — опция -n 636 NOG 841 nom 225, 229, 289, .359, 720, 847 Normal 841 offset 384, 561, 686, 847 oldlinks 250, 251, 290, 847 opttest 290, 727, 847 — опция -а 727 — опция -b 727 paircheck 446, 847 passmass 160 phone 791, 848 pick 610, 853 - pipegrep 417, 848 — опция -1 417 - pushin 384, 386, 848 - rcsegrep 412, 414, 415, 849 - rcsegrep.fast 415, 849 — опция -n 415 - rcsfgrep 414, 415, 849 - rcsgrep 408, 414, 415, 849 — опция -а 313, 415 — опция -е 415 — опция -f 415 — опция -1 415 — опция -s 415 - rcsless 313, 380, 853 - rcsmore 313, 380, 853 - rcspg 313, 380, 853 - rcsrevs : 313, 415, 849 - recomment 479, 557-559, 849 - redo 183, 184, 849 - relink 287, 849 - rename 284, 287, 849 - Rewid : 841 - rilp 160 - ruiised 69, 528, 529, 849 - runtime 411, 414, 617, 849 - script.tidy 768, 831, 850 - sedman 695, 850 - showraatch 400, 850 - si 282, 319, 851 - source.pt 870, 873 - squoze , 372, 851 - stree .' 243, 244, 851 — опция -а 243 - stripper' 370, 851 - su 343, 851 - sysmgr 718 - termtest 675, 852 — опция -b 675 - tgrep 417, 852 — опция -I 417 - tm 91, 852 - ToStatus 841 - tputinit 85, 852 - triplespace 384, 385, 852 - vgrep 229, 852 - vless :... 380, 853 - vmore 380, 676, 853 - vpg : 380, 853 - watchq 608, 853 - whereiz 72, 90 - wordfreq 445, 853 — опция -i 445 - xgrep 401, 853 - zap 609, 610, 853 - zed 368, 853 Предметный указатель 905
— zex 853 — zless 364, 380, 853 - zloop 364, 367, 853 — zmore 364, 380, 853 - zpg 364, 380, 719, 723, 724, 853 - zvi 364, 368, 853 Табуляция, символы: — обработка в системе 651-653 — обработка в терминале 649 Телетайп (tty) 646, 779, 827, 830 Терминал: — база данных 74 — виртуальный 648 — драйвер 133, 150, 384, 620, 654, 655 — зависание 79, 666 — инициализация 74, 75, 81, 84, 85 — автоматическая 56, 58 — настройка 645, 656 — управляющие последовательности 656 — обработка символов 648 — табуляции 649 — описание 658 — определение имени 83 — очистка экрана 340 — потеря связи 666 — прекращение ввода 666 — проблемы 664 — режим обработки данных 135 — режимы 647 — строка состояния 99 — тип 47, 49 — задание 75-77 — управление 646 — управление потоками 650 — управляющий 602, 603, 609 — установки 49, 58, 651 — Escape-последовательности 81, 665 Типографский набор 679, 688-690 — технология WYSIWYG 689 Указатель файла 754 Управление заданиями....21, 39, 134, 186, 189, 191, 192, 342, 470, 599-601, 609, 611, 830 Управление потоками 650 - аппаратное 650 - в редакторе Emacs 507 - режим ENQ/ACK 650 - режим RTS/CTS 650, 651 - режим XON/XOFF 650 Устройство: - /dev/nuU 204, 634 - /dev/zero 205 Ф Файл: — автоматическое обновление отчета 320 — автоматическое удаление 65 — автосохранения 501 — архивный 29, 308, 868 — блокировки 768 — владелец 326, 328, 360 — восстановление 302, 303 — временные характеристики 234, 252, 259, 318, 323 — временный 65, 145, 147, 155, 241, 314, 317, 476, 719, 744, 770 — автоматическое создание 148 — время последнего доступа 234, 317 — время последней модификации 231, 234, 247, 317, 433 — дампа 162, 226, 306, 358, 363, 605, 606, 826 — двоичный 420 — дескриптор 42, 599, 612, 738, 749, 779 — закрытие 779 — дополнение имени 93, 134, 140, 141, 182, 216 — заголовков > 876 — запись 779 — защита 325, 332 — извещение об изменении 319 — извлечение из shar-архива 872 — извлечение из tar-архива 871 — извлечение из сжатого архива 296 — изменение прав доступа 330 — имя 26, 314, 324 — индексный дескриптор 34, 234, 244, 253, 257, 262, 271, 278, 298, 345, 355, 356, 820 — виртуальный 271 — исполняемый 239, 370, 736 — конфигурации 23, 47, 49, 52, 74, 79, 82, 88, ПО, 115, 173, 179, 190, 199, 220, 221, 226, 315, 322, 329, 841, 850 — и путевые имена 52 — копирование 274, 283, 298, 322, 323, 758 — локальный 857 — наложение "заплат" 516, 873 — низкоуровневая обработка 561 — общий 857 — объектный 29, 306 — ограничение размеров 362 — определение типа 383 — открытие 749 — открытый 42, 361, 740 — очистка 360 — паролей 23, 35 — переименование 275, 283, 284, 286 — в интерактивном режиме 286 — перемещение 275, 322 — подсчет по типам 247 906
поиск 218, 248, 271, 273 — двойников 245 — по временным характеристикам 259 — по заданному слову 270 — по имени владельца 264 — по имени группы пользователей 264 — по правам доступа 264 — по размеру 263 — по содержимому 269 — по типу 263 — ссылок 271 понятие 41 постраничный просмотр 378-380 построчная обработка 754 права доступа 35, 262, 264, 325, 351, 711, 768 — в BSD UNIX 341 — изменение 341 произвольного размера 834 просмотр содержимого 377 пустой 360 разбиение в фиксированной точке 563 разбиение с учетом контекста 565 размер 247 разреженный 373 распаковка 871 расширение имени 29, 70 регистрации 361 резервный 306, 501 с несколькими именами 276 с "нулевым" именем 353 сжатый 29, 363, 367, 368, 380, 381, 853 скрытый 226, 238, 355, 740, 798, 826 создание 136, 378 сравнение 283, 422 сравнение имен 246 текстовый 41 текущий 465 только для чтения 332 удаление 27, 322, 345, 351-354, 357, 359 — безопасное 348, 349 — выборочное 347 — интерактивное 347 — по номеру индексного дескриптора 355 — случайное 349 указатель 754 упакованный 29 управление доступом 325 ускоренный поиск 267 центрирование строк 563 чтение 779 шифрование 339 .bashjiistory 179 .bashjogin 48, 56 — .bashjogout 48, 850 — .bash_profile 48, 101 — .bashrc 48, 56, 101, 632 — .cronrc 778 — .cshrc 48-50, 52, 54-56, 58, 60, 65, 79, 88, 92, 93, 95, 96, 98-101, 103, 115, 161-163, 170, 180, 190, 213, 216, 218, 219, 221, 227, 276, 318, 343, 344, 363, 464, 617, 620, 632, 734, 736, 817, 820 — .emacs 501, 503-506, 508 — .emacs_ml 838 — .exic 72, 452, 455, 462, 463, 465, 472-477, 482, 488, 490, 494-497, 514, 672, 676 — .history 94, 179, 180 — .ispell_words 442 — .kshjiistory 179 — .kshrc 52 — .login 48-53, 56, 58, 59, 65, 75, 78, 82, 89, 99, 102, 115, 163, 179, 180, 191, 276, 326, 344, 599, 620, 665, 795, 833, 846 — .logout 48, 52, 63-67, 149, 180, 315, 609, 613 — .profile 48, 49, 52, 53, 56, 57, 59, 60, 64, 75, 78, 79, 82, 89, 99, 101, 105, 115, 179, 182, 184, 191, 213, 218, 320, 344, 833 — .rhosts 44, 303 — .tcshrc 49 — /dev/null 204, 634 — /etc/group 335 — /etc/passwd 335, 626, 815, 826 — /etc/rc 778 — /etc/shells 60 — /etc/ttytab 57 — /etc/utmp 827 — /usr/dic/words 442 — 80cols 673, 839 — ascii 829 — crontab 349, 357, 361, 633, 635-638, 640, 795, 841 — cshjnit 838, 841, 850 — cshjogout 842 — exrc 464, 843 — ispell.hash 441 — longlines 671, 675, 846 — make_print 846 — makefile 321, 370, 434, 435, 846-, 874, 878 — no_run 847 — passwd 80 — qcsh 848 — screensize 674, 675, 850 — search.el 504, 850 — sedscr 840, 849 — sh_init 838, 84J, 850 — shjogout 850 — spellhlst 441 — tags 452, 472, 473 Файловая система 209 — идентификатор 271 — иерархическая 31, 274 Предметный указатель 907
— перемещение файлов 275 _ сетевая 44, 80, 273, 276, 277, 349, 362, 366, 740, 857 — структура 31 — hsfs 855 Фильтрация текста 465 — в редакторе ех 465, 466 — в редакторе vi 465-467 Фильтры 17, 42, 751 Форматирование документа 684 Функция 168 — имитация в Bourne shell 169 — с 216 — gethostbyname 593 — getpwuid 593 — line 721 — printf 421, 471, 619, 744 — qcsh '. 225 — readdirs 593 — signal 720 — sprintf 593 — strcpy 619 — sqrt 877 Хронометрирование 614 Циклы :.-19, 718 - for. 19, 2), 73, 88, 141, 142, 144, 203, 238, 528, 688, 710, 723, 725, 733, 741, 746, 747, 763, 767, 770, 774, 792, 819 — с несколькими переменными . 747 - foreach 141, 142, 144, 688, 778, 787 - until 718, 729, 769 - while 88, 105, 261, 380, 622, 710, 718, 723, 725, 729, 733, 739, 747, 749, 754, 766, 770, 785, 787 Ш Шифрование файлов 339 э Электронные таблицы 812 Эпоха 828 Эхо: - удаленное 646 - выключение 760 39 200, 785 640, 717 .488 477 785 199 775 .148 Ядро. .25 — запуск процессов 749 — запуск сценариев 734, 737 & (амперсанд): — запуск фонового процесса — оператор,..: 148, 188, 191, 198, — &&, оператор 65, 178, 289, | (вертикальная черта): — в редакторе ех — в редакторе vi 156, 476, — оператор 195, 196, 199, 717, 749, 775, — |Л; оператор 196, — ||, оператор 717, ! (восклицательный знак): — автоматическое создание временных файлов — в операторе сравнения 77 — в перечне введенных команд...- 173 — команда редактора ех 470 — команда редактора sed 546 —. оператор 165, 289 — опция команды find 254, 258 — переменная 782 — сценарий 134, 148, 431, 839 — !!, оператор : 174, 177, 419 — !$, оператор 163, 171-173, 175 — !*, оператор 163, 175 — !:, оператор 172, 174 — !Л, оператор 175 , (запятая): — в именах файлов. 27 * (звездочка): — в регулярных выражениях 393, 394, 396, 397 — специальный символ 26 > (знак больше): — оператор 94, 195, 198, 199, 202, 308, 328, 749, 754, 785 — >&, оператор 195, 198 — », оператор 195, 199, 202 ? (знак вопроса): — и быстрая команда find 267 — переменная 94, 715 — специальный символ 28 * (знак вставки): — в регулярных выражениях 394 — оператор 173, 289 — АА, оператор 172 * (знак диеза): — команда редактора sed 550 — символ комментария 216 — #!, оператор 385, 386, 563, 713, 732, 734-737, 770, 826, 838 $ (знак доллара): — в именах переменных 93 — в регулярных выражениях 394 — оператор 142, 779 — $0, оператор 147, 765 908
< (знак меньше): — оператор... 128, 144, 145, 195, 196, 292, 443, 571, 694, 785 — «, оператор (конструкция "документ здесь") 114, 128, 144, 145, 195, 292, 637, 640, 733, 759 % (знак процента): — в номерах заданий 187 — в редакторе ех 466 — в строке приглашения 95 — %?, оператор 188 = (знак равенства): — в редакторе sed 550 : (двоеточие): — команда редактора sed 550 — оператор .738, 739, 745 — разделитель списков 73 — (дефис): — в начале имени файла ....: 353 — вместо имени файла 204 — опция 215, 344 / (косая черта): — в путевом имени 210 — в редакторе vi : 470 — и быстрая команда find 267 — как имя корневого каталога 32 () (круглые скобки): — оператор 148, 200-202 \ (обратная косая черта): — в начале Escape-последовательности 114 — в Bourne shell 122, 126 — в С shell .- 126 — защита специальных символов 133, 134 — и подстановка команд 112 _ (символ подчеркивания): — в редакторе vi 475 @ (символ "эт"): — оператор С shell 137, 785, 786 — ©-функции 482, 484-486 (тильда): — в именах каталогов 34, 255 — в конце имени файла 220, 266 — в С shell 255 — как имя начального каталога 219 . (точка): — в именах фалов 28 — в путевых именах 33 — в регулярных выражениях 393, 395 — команда 731, 757 — команда редактора vi 459 — ссылка на текущий каталог 278 — .., ссылка на родительский каталог 278 ; (точка с запятой): — в командной строке 113 {} (фигурные скобки): — в именах переменных 93 - в bash , 136, 225 - в Bourne shell 225, 848 - в С shell 136, 142, 225, 566, 784, 848 - в Кот shell 225, 848 - в tcsh shell 136 - оператор 139, 201, 224, 265, 288, 758 - оператор команды find 260, 265 ASCII-коды 829 В bash (см. Boume-again shell) Bourne shell (sh) 82, 94, 109, 142, 170, 225, 242, 292, 464, 505, 555, 562, 622, 624, 625, 714, 728, 739, 761, 782, 795, 838, 841, 848, 850, 866 — арифметические операции 811 — архивы 291, 293 — shar-архивы 873 — выбор регистрационного интерпретатора 60 — выход из системы 63 — случайный 66 — вычисление выражений 783 — генератор случайных чисел 743 — задание времени 91 — задания: — отложенное выполнение 632, 634 — периодическое выполнение 639 — запуск 833 — в регистрационном режиме 53 — и язык Perl 591 — имитация команды dir 241 — использование кавычек 122, 124-126 — каталоги: — быстрая смена 213 — временный переход 200 — вывод содержимого 235 — изменение текущего 731 — начальный 219 — смена 217, 221 — командная строка: — анализ 118, 726 — аргументы 385, 386, 722-724, 746, 774 — длинная 134, 151, 152 — интерпретация 112 — команды: — встроенные 775 — группирование 200, 201 — интерпретация 109 — код завершения 94, 715, 716 — конфигурирование 94 — многострочные 143 — определение версии 820 — ошибки поиска 826 Предметный указатель 909
— перечень введенных 185 — повторное выполнение 141 — поиск 89 — последовательность поиска 116 — сигналы прерывания 720 — уникальность имен 730 конструкция "документ здесь" 128, 144, 733, 759 ■ массивы 767, 787 — имитация 733 • метасимволы 226, 230 Многозадачность 186 ■ операторы: — подстановки параметров 139 — список 128 определение 22 опции: — -е 728, 759, 773 — -i 204 — -v 93, 204, 283, 759 — -х 93 ошибки: — поиска команд 826 — обработка 783 пакетное редактирование 515, 516 переадресация ввода-вывода.. 115, 121, 195, 197, 202, 751, 757, 771, 780 переменные: — получение списка 92 — синтаксис определения 782 — трассировка 127 — удаление 92 переменные среды 87, 89 переход в другую учетную запись 344 подстановка параметров 744 поиск: — в файлах документации 819 — команд 89 — файлов 261 преждевременное завершение программы 667 преимущества и недостатки 712 прерывание работы 66 проведение конференций 203 программирование 710, 712, 732 процессы: — вывод списка выполняющихся 621 — уничтожение 609 — уничтожение фоновых 612, 613 — управление 598 — фоновые 39, 40 псевдонимы: — имитация 169 — команды find 272 сигналы 104, 738, 781 — обработка 606 — прерывания команд 720 — специальные символы 122 — защита 781 — подстановка 127 — список 128 — сравнение с bash 48 — сравнение с С shell 778, 784 — сравнение с Кот shell 48 — строка приглашения 22, 99, 105, 713 — вторичная 88, 94, 124, 143 — первичная 88, 94 — строки: — выделение частей 763 — обработка очень длинных 655 — разбиение 576 — сценарии 528, 711, 713, 734, 737 — отладка 770, 772 — терминал: — автоматическая инициализация 56 — зависание 79 — переустановка размеров экрана 673 — установка типа 75 — файлы: — временные 315 — дескрипторы 206, 733, 738, 779 — запись 779 — конфигурации 48 — переименование 285 — поиск 261 — построчная обработка 755 — скрытые 226 — слежение за изменением 320 — создание 317 — создание пустых 360 — сценариев 29 — удаление временных 65 — чтение 779 — .сгопгс 778 — .profile 344 — /etc/rc 778 — функции 165, 168, 178, 876 — имитация 169 — хронометрирование 614 — циклы 718, 747, 749 — инкрементирование счетчика 741 — чтение данных с клавиатуры 721 Boume-again shell (bash) 114, 136, 225, 838 — автоматическое создание временных файлов 148 — выбор регистрационного интерпретатора 60 — выход из системы: — случайный 66 — задания: — отложенное выполнение 632 — управление 191 — запуск: — в порожденном процессе 101 910
— в регистрационном режиме 53 — ускорение 56 инсталляция 862 каталоги: — быстрая смена 213 — изменение текущего 731 — копирование 288 — начальный 219 — смена 214, 216, 221 — создание 71 — текущий 220 командная строка: — аргументы 746 — интерпретация 119 — редактирование ПО, 135, 181 команды: — выполнение 105, 106 — перечень введенных ПО, 170, 171, 173, 179, 181, 183-185 — подстановка 137 — подстановка результатов выполнения 765 — поиск 72 — псевдонимы 161, 165 — уникальность имен 730 массивы 111 метасимволы 224 операторы: — список 128 О 222 — :г 171 описание 109 определение 22 опции: — -е 777 login 61 — -х 148, 149 переадресация ввода-вывода 195, 199 почтовые сообщения 320 процессы 134, 147 — подстановка 134, 147 — управление 598 создание путевого имени 34 специальные символы: — список 128 сравнение с Bourne shell 48 сравнение с С shell 48, 778 сравнение с tcsh shell ПО строка приглашения 95, 97, 100, 104, ПО, 713 — многострочная 98 — со значками эмоций 835 строки: — разбиение 576 — редактирование 97, 139, 745 терминал: — автоматическая инициализация 58 — файлы: — безопасное удаление 173 — дополнение имен 140 — конфигурации 48 — ограничение размеров 363 — скрытые 226 — сравнение 431 — функции 168 — GNU-версия 839 С shell (csh) 26, 55, 92, 104, 109, 238, 318, 319, 505, 615, 622, 633, 673, 688, 837, 838, 841, 852, 866 — вектор слов 120 — вставка символов новой строки 548 — выбор регистрационного интерпретатора 60 — выражения 784, 785 — вычисление 782 — выход из системы 63 — случайный 65, 66 — генератор случайных чисел 743 — задание времени 91 — задания: — остановка 190 — отложенное выполнение 632, 634 — периодическое выполнение 639 — управление 191 — запуск 833 — в порожденном процессе 101 — в регистрационном режиме .• 53 — ускорение 54, 56 — использование кавычек 124-126 — каталоги: — быстрая смена 213, 216 — временный переход 200 — идентификация начального 343 — изменение текущего 731 — копирование 288 — начальный 219 — смена 214, 216, 217 — создание 71 — текущий 220 — bin 69 — командная строка: — анализ 781 — аргументы 774 — интерпретация 112, 119, 120 — редактирование 181 — команды: — встроенные 780 — группирование 200 — интерпретация 109 — код завершения 94, 716 Предметный указатель 911
— конфигурирование 94 — многострочные 143 — несовместимость 780 — определение версии 820 — перечень введенных 93, 170, 171, 173, 179-181, 183, 184, 419, 499, 832, 849 — повторное выполнение 141, 157 — подстановка 137 — поиск 89, 90, 162 — последовательность поиска 116 — псевдонимы 161, 163, 167, 168 — таблица поиска 711 конвейеры 722 конструкция "документ здесь" 128, 733, 759 массивы 52, 59, 104, 111, 138, 767, 778, 787 метасимволы 224, 226, 230 многозадачность 186 обработка ошибок 783 операторы 165, 784, 785 — арифметические 786 — запроса информации о файлах 786 — логические 786 — побитовые 786 — присваивания 785 — список 128 — сравнения 786 — :е 138 g : 138 h 137, 242 р 138, 173, 179 q 138 г 137, 171 t 97, 138, 782 х 138 — foreach 142, 144 — if....„ : ......784 -r switch .... V :.... :....'..'....788 определение : , 22 опции: — -с 96 f 50, 736 i 204 — -n 783 v 204 переадресация ввода-вывода 115, 195-199, 202, 751, 780 переменные 784 — получение списка 92 — синтаксис определения 782 — специальные 93 переменные среды 87, 89 переход в другую учетную запись 344 подсчет слов 443 — преждевременное завершение программы 667 — преимущества и недостатки 712 — прерывание работы 66 — проведение конференций : 203 — программирование. 710, 778 — просмотр почты 218 — процессы: — вывод списка выполняющихся 620 — уничтожение.... 609, 611 — уничтожение фоновых 612, 613 — управление 598 — фоновые 39, 40 — регулярные выражения 395 — редактирование строк 97, 137, 139 — рекурсивный вызов псевдонимов 166 — сигналы 738, 781 — создание путевого имени.... 34 — специальные символы 123, 125, 781 — защита , 781 — подстановка 127 — список 128 — сравнение с bash 48, 778 — сравнение с Bourne shell 778, 784 — сравнение с Кот shell 778 — сравнение с tcsh shell 49, 110, 784 — строка приглашения 22, 88, 95-97, 103 — вторичная 142 — многострочная 100 — сценарии 165, 714, 734, 737 — терминал: — автоматическая инициализация 56 — зависание 79 — задание типа 76, 77 — определение версии 820 — определение типа 83 — переустановка размеров экрана 673 — управление ходом вьшолнения программ 780 — файлы: — безопасное удаление 173 — дескрипторы 779 — дополнение имен .'. 140, 182 — запись 779 — конфигурации ; 48-50, 52, 778, 787 — ограничение размеров 362 — переименование 285 — поиск 250 — слежение за изменением 318 — создание пустых 318, 360 — сценариев 29 — удаление временных 65 — чтение 779 — форматирование строк комментариев 560 — хронометрирование 614 csh (см. С shell) 912
dbm, пакет программ 374 ed, редактор 88, 422, 424, 429, 477, 509 — адресация строк 510 — команды 511 — метасимволы 403 — пакетное редактирование 515 — поиск слов 398 — регулярные выражения 394, 395, 406 — файлы: — ограничения на размер 516 — редактирование копий 514 — редактирование сжатых 368, 853 Emacs, редактор 184, 238, 344, 379, 505, 584, 688, 695, 711, 801, 807, 837, 838 — взаимодействие с терминалом 75 — выравнивание строк 558 — глобальная инициализация 501 — каталог для хранения программ 69 — команды: — дополнение 503 — досрочный ввод 150 — поиска ~ 850 . — abbrev-mode 506 — doctor. 508 — edit-word-abbrevs 507 — enable-flow-control 507 — overwrite-mode 502 — psycboanalyze-pinhead .508 — revert-bufter 501 — undo 501 — write-abbrev-file 507 —, макросы 500 — настройки , 501 — обработка очень длинных строк 655 — обработка сигналов 606 — описание 451 — опции: — опция -q 501 — опция -и 501 — перенос строк ...675 — поиск текста 504 — преимущества по сравнению с другими редакторами 499 — регулярные выражения 399, 404 — редактирование командной строки ПО, 135, 182 — режимы 500 — автозамены 506 — вставки 502 — замены 502 — резервные копии 27, 266 — удаление 357 — сокращения слов 506 — сравнение с vi 499 — управление потоками 507 — управляющие символы 505 — файлы: - автосохранения 501 - временные 220 - дополнение имен 182 - права доступа 328 - редактирование сжатых 369 - резервные 306, 501 - удаление временных 162 - .emacs 501, 503-506, 508 — GNU-версия 499, 842 entomb, система восстановления файлов 349 ех, редактор 429, 451, 488, 509, 745, 843 — адресация строк 510 — буфер обмена 454 — буфер хранения 461 — глобальные изменения 461 — изменение регистра символов 462 — инициализация 72 — интерпретация клавиш 487, 490 — команды: - глобальные 457 - список 511 - а 511 - аЬ 473, 474, 481 - args 454 - с 512 - со 512 - d 511,512 - е 454, 477, 489 - е! 458, 468 - f. 454 - g 458, 512 - g! 458 - i 512 - m 512 - mo 460 - n 454 - p 512 - q 459, 512 - q! 72, 464, 469 - r 96, 513 - r! 468 - s 511, 513 - se 468 - set all 464 - set nowrapscan 462 - set number 462 - so 456, 489, 513, 514 - t 512 - tag 472 Предметный указатель 30 9-171 913
_ v : 458 — vi '. 495 _ w .454, 458-460, 462, 467, 470, 489, 495, 513 459, 469 — конфигурирование 88 — локальные установки 455 — метасимволы 403 — написание сценариев 510, 514 — описание 451 — опции: — autoindent 511 — modeline 463, 464 — modelines 463, 464 — nomodeline 464 — nomodelines 464 — открытый режим 84, 183, 477 — пакетное редактирование 515 — перемещение текста 460 — подсчет слов 462 — подтверждение изменений 458 — поиск: — глобальный 458 — по шаблону 457 — слов 398 — проверка правописания 437 — регулярные выражения 406, 407 — сравнение с sed 527 — строки режима 463 — файлы: — возвращение к сохраненной версии 454 — восстановление 468 — добавление текста 460 — ограничения на размер 516 — редактирование копий 514 — редактирование нескольких 453 — редактирование сжатых 368, 853 — сохранение части 459 — фильтрация текста 465, 466 fileutils, пакет программ 843, 869 ftp-сеанс, анонимный 128 FTPMATL, почтовый сервер 866 glimpse, пакет программ 408, 412, 844 н High Sierra, формат 854, 855 I ISO 9660, стандарт. 854, 855 К Кот shell (ksh) 109, 229, 838 — выход из системы 63 — случайный 66 — задания: — отложенное выполнение 632 — управление 191 — запуск: — в порожденном процессе 101 — ускорение 56 — каталоги: — быстрая смена 213 — изменение текущего 731 — начальный 219 — смена 214, 221 — командная строка: — аргументы 722, 746 — редактирование 135, 181-183 — режим редактирования 88 — команды: — встроенные 775 — интерпретация 109 — перечень введенных 170. 171, 179, 181-184, 499 — повторное выполнение 141 — подстановка результатов выполнения 764 — поиск 72 — псевдонимы 161, 165 — уникальность имен 730 — массивы 767 — метасимволы 224 — операторы: — список 128 — определение 22 — отображение управляющих символов 654 — переадресация ввода-вывода 121, 195, 199 — подсчет слов 443 — почтовые сообщения 320 — преимущества и недостатки 712 — процессы: — управление 598 — фоновые 39 — создание путевого имени 34 — специальные символы: — список 128 — сравнение с Bourne shell 48 — сравнение с С shell 778 — строка приглашения 97, 713 — строки: — разбиение 576 — редактирование 97, 139, 182, 745 — терминал: — автоматическая инициализация 58 914
— файлы: — дополнение имен 182 — инициализации 88 — конфигурации 48 — ограничение размеров 363 — скрытые 226 — функции 168 ksh (см. Кош shell) LATEX, пакет макросов 689 м man, пакет макросов 691, 822, 823, 839, 844 те, пакет макросов 691, 696, 845 mm, пакет макросов 691, 696 ms, пакет макросов 553, 691, 693, 696, 698, 839, 844 N netpbm, пакет программ 679, 701, 839, 847 NIS (Network Information Service) 44 pbmplus, пакет программ 701, 865, 847 pdksh shell 100 Perl, язык 261, 282, 311, 354, 415, 588, 700, 848, 868 — изменение объекта символической ссылки 287 — переименование файлов 284, 286 — поиск текста 416, 417 — поиск текстовых файлов 250 — преимущества 417, 588, 590 — проверка синтаксиса 447 — проверка целостности ссылок 251 — регулярные выражения 592 ■- Peri 5 588 — преимущества 593 PostScript, язык 679, 698, 798 PSUtils, пакет программ 699, 700, 848 pty (псевдотерминал) 66, 604, 342, 645, 648, 655, 827, 830, 850 re shell 598 RCS, система управления версиями.. 172, 178, 310, 311, 313, 380, 410, 412, 414, 423, 510, 517, 837, 848 — команда: - ci 311, 793 - со 311, 313, 415 - rcsdiff 313 - rcsmerge 312 - rlog 312, 313 SCCS, система управления исходными текстами 299, 306, 310, 421, 423, 427, 430, 436, 517 — команда: - check 311 - create 310 - delta 310 - edit 310 - get 310 - unedit 311 sed, редактор 20, 57, 73, 104, 182, 203, 217, 235, 238, 242, 245, 249, 251, 266, 370, 371, 417, 419, 509, ^527> 532, 588, 687, 694, 695, """697, 709, 721, 762, 780, 792, 797, 819, 830, 831, 838, 840, 844, 849 — адресация 529, 549 - строк 510 — версия gsed 547 — вставка символов новой строки 548 — выделение макросов troff. 696 — вызов 527 — длинные командные строки 143 — дублирование дерева каталогов 265 — и язык Perl 589, 591 — имитация команды grep 408 — команды: - замены 768 - синтаксис 549 - список 550 - а 550 - Ь 546, 547, 549, 551 - с 551 - d 530, 544, 545, 551, 736 - g 536, 538, 541, 547, 551, 552 - G 385 - h 536, 538, 541, 547, 552 - i 390, 553 - 1 250. 390, 553 - N 531, 539, 544, 549, 553 - p 528, 554, 764 - q 250, 385, 547, 548, 554 - r 549, 554 - s 533, 547, 549, 554 - t 547, 549, 555 - w 549, 555 - x 536, 541, 556 - у 535, 537, 556 — краткий справочник.: 549 — метасимволы 404, 533 — непосредственный запуск 385, 386 — несколько соответствий шаблону в строке 535 — область хранения 532, 535, 537, 541 — область шаблона 531, 535, 537, 539, 541, 545 Предметный указатель зо» 915
— ограничения • 516 — опции: — -е 528, 549 — -f 385, 386, 528, 549, 735, 841 п 228, 528, 549, 764 — отличия от других редакторов 527 — пакетное редактирование 514, 518 — подсчет количества повторений слов 445 — поиск: -■■ в RCS-файле 415 — комбинированный 418 — многострочный 415 — повторяющихся слов 398 — путевых имен 273 — создание базы данных 268 — текста 411 — получение итога по столбцу 811 — преобразование текста 535 — работа с архивами 293 — разделители 532, 768 — регулярные выражения 139, 394, 395, 398, 401, 406, 407, 529, 532, 534, 696 — редактирование строк, не соответствующих . шаблону _ _ 546 — создание отступов: — в тексте ..563 — для печати 685 — строк 384 — сравнение с ех 527 — сравнение с vi 527 — сравнение структуры каталогов 246 — строки: — выделение частей 763 — обработка пустых 384 — преобразование частей 537 — разбиение , 577 — сортировка подлине 587 — удаление нескольких 545 — сценарии: — написание _ 510 — невыполняемые 69 — порядок команд 531 — удаление пробелов _ 386 — файлы: — переименование 283 — преобразование имен _ 759 — работа с версиями 313 — редактирование путевого имени 97 — создание списка 228 — форматирование: — исходных текстов 558 — строк комментариев ....559 — текста 558 — шаблоны, расположенные в разных строках 543 sh (см. Bourne shell) sharutils, пакет программ ..850 Shell 16 — архивы 252, 291, 293, 294 — виды „.. 21, 598, 712 — запуск: — в порожденном процессе 101 — нескольких экземпляров 192 — программ 712 — командная строка: — интерпретация .■ 16, 112 — команды: — внешние 24 — выполнение 712 — интерпретация 109, 111 — назначение 109 — настройка 47 — определение 712 — остановка 192 — переменные 86, 92 — порожденный 48, 200, 600, 613 — программирование 19, 709, 732 — регистрационный 47, 53, 833 — специальные символы 27 — сценарии 24, 710, 712, 713 — отладка 770 —■ передача аргументов 1 19 — удаленный 44 — файл конфигурации 47, 49, 52, 199, 833 — функции ...... .134 shellutils, пакет программ .....850 Tel, язык сценариев , 158, 851 tcsh shell 109, 136, 229, 289, 346, 852 — запуск в порожденном процессе 101 — командная строка: — редактирование 181 — команды: — перечень введенных , 183 — массивы '. ,...111 — метасимволы .224 — операторы: — список 128 — описание '. _ НО — определение 22 — остановка задания 190 — отображение управляюших символов , 654 — переадресация стандартного вывода 199 — смена каталога 215 — специальные символы: — список , 128 — сравнение с bash ПО — сравнение с С shell 49, ПО, 784 — строка приглашения 95, 97, 101 916
— терминал: — зависание — управление процессами 598 — файлы: — дополнение имен 140 — конфигурации ;...49 — скрытые .226 — удаление 346 textedit, редактор 306 textutils, пакет программ 842-843, 852 ik, язык сценариев 851 transcript, пакет программ 699 tty (последовательный порт)... 67, 598, 604, 779, 827, 830 iwm, менеджер окон 675 и UNIX: — версии 7 -w- недостатки.... 44 — особенности 15 V vl, редактор.... 81, 88, 146, 184, 189, 203, 219, 321, 324, 344, 349, 378, 379, 451, 558, 559, 668, 688, 690, 695, 711, 745, 801, 807, 837, 840, 843 — автозамена слов 473-475 — буфер обмена 453, 454 — версия edit 745 — версия view 379, 745 — взаимодействие с терминалом 74 — временный выход в shell _ 469 — вставка текста в окно 486 — выравнивание текста 479 — глобальные изменения ..461 — задание шага табуляции 653 '— запуск в порожденном процессе 600, 738 — изменение регистра символов 462, 494 — инициализация 72 — использование мыши 452 — клавиши: — интерпретация 665 — многофункциональные 473 — неиспользуемые 482 — переназначение 481, 482, 484, 486-489, 494, 497, 662 — код завершения 719 — команды 135 — досрочный ввод 150 — инициализации 94 — регистрация выполняемых ;...' 831 — редактора, ех 511 — символьные 475 — создание пользовательских 481 — строковые 475 — удаления строки ;.: 135 — А 483 — ciags 452, 472 — d : 456, 475 — h 495 — j 478, 483, 495, 498 — k 478, 495 — 1 495 — m 473, 480, 497 — map 481, 482, 484, 488, 489, 494, 496 — map! 481, 482, 484, 488, 495 — n 459, 471 — О 483 — open 478 — p 456, 457 — Q 478, 495 — set directory , 476 — u 457, 458, 467, 480, 484, 496, 497 — unmap! -,.......483 — w 189, 466 — x ■. 456 — y.: 456, 475, 497 — ZZ ., ; 469, 477 конфигурирование 88 копирование текста 454, 456 локальные установки 455 метасимволы 403 обработка: — пустых строк 384 — сигналов 606 — символов :..... '. 568 описание '...'. 451 опции: — -г 468, 469 — autoindent 464, 486 — autowrite 190, 468 — modeluie 463, 464 — modelines 463, 464 — noautowrite „ 468 — nomodeline 463 — nomodelines 464 — noremap 497 — nowrapscan ;. 472 — remap 490 — showmatch 464 — wrapmargin 464, 486, 494, 498 — wrapscan 465, 476 отмена результатов редактирования 457, 480, 497 переменная window 672 перемещение курсора 495, 496 перемещение текста 456, 460 подсчет слов 462 подтверждение изменении 458 поиск: — комбинированный 470 Предметный указатель 917
— определений функций 472 — отключение повторного 462 — по шаблону 457, 458 — слов 398 проверка правописания 437, 439 проверка размеров экрана 674 работа с оконными системами 671 работа с электронными таблицами 812 разбиение длинных строк 498 разделители 532 регулярные выражения 394, 395, 398 редактирование: — командной строки 110, 181 — нескольких файлов 453 — перечня введенных команд 183 — сжатых файлов 368, 853 — списка аргументов. 147 — списка файлов 155, 157 режим: — автоэамены 506 •— ввода текста 482, 495, 496 — выполнения 40 — командный 481 — открытый (редактора ех) 84, 183, 477 сложные макросы 497 сообщения об ошибках 676 сравнение с Emacs 499 сравнение с sed 527 строка приглашения 96 — строки режима 463, 464 — сценарии редактора ех 514 — файлы: — конфигурации ■. 162 — восстановление 468, 469 — временные 476 — добавление текста 460 — переименование 286 — права доступа 328 — редактирование нескольких 453 — редактирование сжатых 368, 853 — редактирование списка 155, 157 — создание 136 — файлы установок: — использование нескольких 464, 465 — фильтрация текста 465-467 — ©-функции 482 — комбинирование 485 — определение 484-486 W WYSIWYG, технология 689 X Window 15, 26, 42, 56, 66, 89, 321, 572, 599, 621, 629, 648, 673, 675, 808 XII, пакет программ 281 918
Содержание Как пользоваться книгой 5 Предисловие 7 Глава 1. Введение 15 1.01 Особенности UNIX 15 1.02 Кто воспринимает пользовательский ввод? 16 1.03 Одновременная работа программ 17 1.04 Использование каналов для создания нового инструментального средства 18 105 Программирование в shell 19 1.06 Инструменты редактирования 20 1.07 Возможности системы — в ваших руках 20 108 Виды интерпретаторов shell 21 109 Определение вида интерпретатора 22 1.10 Внутренние и внешние команды .ч 23 1.11 Выполнение внешних команд интерпретатором shell...... Ц. 24 1.12 Назначение сценариев shell .....V, :.ч. 24 1.13 Почему важно знать основные понятия? д. .\. 25 1.14 Ядро и демоны X..., 25 1.15 Имена файлов .\ .\ 26 1.16 Специальные символы shell \. 27 1.17 Расширения имен файлов .^. 29 1.18 Обработка специальных символов >,„ ':,. 30 1.19 Структура файловой системы ,.'.'. ,\. 31 120 Ваш начальный каталог .\.. 32 121 Создание путевого имени ,. ,! .^ 33 1.22 Как UNIX следит за файлами: индексные дескрипторы 34 1.23 Права доступа к файлу ':., 35 124 Суперпользователь (пользователь root) 38 125 Доступ к каталогам », '. 38 1.26 Что можно делать в многопользовательской системе......,....^ 39 127 Как выполняется фоновый процесс? i ".-.. 39 1.28 Ошибки при выполнении фоновых процессов ;..,. 40 129 Когда файл не является файлом... :.■... 41 130 Переадресация ввода-вывода .V.Vs^. 42 1.31 Система X Window ;;?:.„, 42 Содержание 919
122 Белые пятна , 42 133 Сети и UNIX 43 1.34 Недостатки UNIX 44 Часть первая. Работаем дома 45 Глава 2. Вход в систему 47 2.01 Настройка shell 47 2.02 Файлы конфигурации интерпретатора shell: что, где и почему 47 2.03 Что происходит при выполнении файлов конфигурации shell? 49 2.04 Изменение конфигурации пользовательской учетной записи 49 2.05 Как ускорить процедуру входа в систему? 50 2.06 Использование полного путевого имени в файлах конфигурации 52 2.07 Выборочное чтение файлов конфигурации 52 208 Распознавание регистрационного интерпретатора shell 53 2.09 Ускорение запуска С shell 54 210 Ошибки при проверке значения переменной prompt 55 2.11 Ускорение запуска ksh и bash 56 2.12 Автоматическая инициализация различных терминалов 56 213 Файл .cshrc.SHOST для инициализации различных серверов 58 214 Сценарий motd.diff: новые строки в регистрационном сообщении 58 2.15 Однократный вывод регистрационного сообщения 59 2.16 Использование незарегистрированного интерпретатора shell 60 Глава 3. Выход из системы 63 3.01 Выполнение команд при выходе из системы 63 3.02 Выполнение команд при выходе из Bourne shell и Кот shell 63 3.03 Электронные приветствия :..... 64 3.04 Автоматическое удаление файлов 65 3.05 Предотвращение случайного выхода из С shell.., 65 3.06 Предотвращение случайного выхода из Bourne shell 66 3.07 Прерывание сеанса при помощи утилиты screen 66 3.08 На каком терминале я работаю? 67 Глава 4. Организация начального каталога 68 4.01 А это обязательно? 68 4.02 Каталог bin для программ и сценариев 68 4.03 Хранение невыполняемых сценариев 69 4.04 Каталоги для хранения программ редактора Emacs 69 4.05 Персональные каталоги 69 4.06 Наименование файлов 70 4.07 Создавайте больше каталогов! 70 4.08 Как ускорить создание каталогов? 71 920
4.09 Инициализация редактора vi при помощи файла .ехгс .....' 72 4.10 Нахождение всех версий команды при помощи команды whereiz 72 Глава 5. Инициализация терминала 74 5.01 Информация о терминалах 74 5.02 Понятие базы данных терминалов 74 5.03 Установка типа терминала при входе в систему 75 5.04 Установка переменной TERMCAP с помощью команды tset 77 5.05 Запрос типа терминала: программа qterm 77 5.06 Если терминал зависает при входе в систему 79 5.07 Чем управляют и чем не управляют базы данных termcap и terminfo 80 5.08 Терминальные Escape-последовательности 81 5.09 Установка кодов удаления символа, удаления строки и прерывания 81 5.10 Какие терминалы можно использовать? - .....83 5.11 Инициализация терминала при помощи команды tset .....84 5.12 Инициализация терминала при помощи команды tput 85 Глава 6. Переменные среды и shell 86 6.01 Для чего нужны переменные среды? 86 602 Отцы и дети 87 £03 Стандартные переменные среды... , 88 6.04 Переменная среды PATH 89 605 Переменные PATH и path 90 6.06 Переменная среды TZ 90 6.07 Который час в Японии? :.... 91 6.08 Переменные shell 92 609 Специальные переменные С shell 93 6.10 Выполнение команд во временно измененной среде ^ 94 Глава 7. Установка строки приглашения 95 7.01 Зачем нужно изменять строку приглашения? 95 7.02 Основные понятия 95 7.03 Проблемы со строкой приглашения в программах vi, rsh и т.д 96 7.04 Быстрая установка строки приглашения с помощью встроенных команд 96 7.05 Многострочные приглашения ...98 7.06 Информация о сеансе в строке состояния терминала 99 7.07 Приглашение с подсказками для начинающих 99 7.08 Выделение символов в приглашении 100 709 Использование переменной SHLVL для отображения уровня порожденного интерпретатора shell 101 7.10 Чем может быть полезна пустая строка приглашения? - 103 7.11 Использование команды dirs вместо переменной cwd в строке приглашения 103 7.12 Инициализация переменных сигналами от внешних команд 104 7.13 Выполнение команд перед выводом приглашения 106 Содержание 921
Часть вторая. Поручите грязную работу компьютеру ш Глава 8. Как shell интерпретирует команды 109 8.01 Назначение интерпретатора shell 109 8.02 Введение в bash 109 8.03 Введение в tcsh 110 8.04 Интерпретация команд и случайная перезапись файлов Ill 8.05 Интерпретация командной строки 112 8.06 Ввод аргументов командной строки 113 8.07 Установка последовательности поиска команд 115 8.08 Создание каталога невыполняемых команд 116 8.09 Специальные символы в псевдонимах команд 117 8.10 Повторный анализ командной строки: команда eval 118 8.11 Какую команду выполнит bash? 119 8.12 Какую команду выполнит С shell? 120 8.13 Особенности перенаправления ввода-вывода 121 8.14 Кавычки и обратная косая черта в Bourne shell 122 8.15 Различия в использовании кавычек между С shell и Bourne shell 125 8.16 Отмена действия специальных символов в именах файлов 127 8.17 Вывод результатов подстановки специальных символов: переменные verbose и echo 127 8.18 Конструкция "документ здесь" 128 8.19 Специальные символы и операторы 128 8.20 Сколько нужно символов обратной косой черты? 133 Глава 9. Как сэкономить время при вводе командной строки 134 9.01 Что нужно знать о командной строке UNIX 134 9.02 Быстрое редактирование командной строки 135 9.03 Повторный ввод командной строки 135 9.04 Метасимволы и создание файлов 136 9.05 Создание строк с помощью оператора {} 136 9.06 Операторы редактирования строк 137 9.07 Редактирование строковых переменных в ksh и bash 139 9.08 Ускорение ввода имен файлов путем их дополнения 140 9.09 Ненужные имена при дополнении имен файлов 140 9.10 Когда не следует дополнять имя файла? 141 9.11 Повторное выполнение команд в цикле foreach 141 9.12 Оператор цикла for 142 9.13 Многострочные команды и вторичные приглашения 143 9.14 Использование конструкции "документ здесь" для создания шаблонов писем 144 9.15 Временные сценарии для сложных команд 145 9.16 Подстановка результатов выполнения команды 145 9.17 Обработка длинного списка аргументов 147
9.18 Подстановка процессов 147 9.19 Для нетерпеливых: досрочный ввод команд 150 1.20 Слишком много файлов в командной строке 151 9.21 Обработка длинных командных строк с помощью команды xargs 152 9.22 Команда xargs: как решить проблемы с символами пробела и новой строки 153 9.23 Слишком много аргументов... Что делать? 154 9.24 Редактирование списка файлов 155 925 Команда repeat в С shell 157 9.26 Программа Expect 158 Глава 10. Псевдонимы команд 161 10.01 Создание пользовательских команд 161 10.02 Псевдонимы для стандартных команд 161 10.03 Псевдонимы С shell с аргументами в командной строке 163 10.04 Псевдонимы в ksh и bash 165 10.05 Сценарии, выполняемые командой source 165 10.06 Предотвращение рекурсивного вызова псевдонимов в С shell 166 10.07 Управляющие конструкции в псевдонимах С shell 167 10.08 Автоматическая расстановка кавычек в псевдонимах 168 10.09 Функции 168 10.10 Имитация функций и псевдонимов в Bourne shell 169 Глава 11. Уроки истории 170 11.01 Чему учит история? 170 11.02 Перечень введенных команд: краткое резюме 171 11.03 Я предпочитаю !$ 171 11.04 Я предпочитаю !:п* 172 11.05 Я предпочитаю ЛЛ 172 11.06 Использование оператора !$ для безопасной работы с метасимволами 173 11.07 Подстановка из перечня 173 11.08 Повторение последовательности команд 177 11.09 Выполнение последовательности команд обработки файла 178 11.10 Просмотр перечня ранее введенных команд 179 11.11 Сохранение перечня ранее введенных команд 179 11.12 Передача перечня ранее введенных команд другому экземпляру shell 181 11.13 Редактирование командной строки 181 11.14 Другие способы редактирования строк из перечня 183 11.15 Изменение символов подстановки команд из перечня в С shell 184 11.16 Альтернатива изменению символов подстановки из перечня 185 Глава 12. Управление заданиями 186 12.01 Основные сведения 186 12.02 Другие способы ссылок на задания 188 12.03 Какое задание является текущим? 189 Содержание 923
1104 Экономия времени при редактировании 189 12.05 Остановка отдельных заданий при перегрузке системы 190 12.06 Извещения об изменении состояния заданий 190 1207 Остановка вывода фонового процесса 191 12.08 Управление заданиями: резюме 191 12.09 Запуск нескольких экземпляров shell 192 Глава 13. Переадресация ввода-вывода 194 13.01 Использование стандартного ввода-вывода 194 13.02 Команде cat недостаточно одного аргумента 196 13.03 Направление в канал только стандартного потока ошибок 196 13.04 Проблемы при пересылке данных программам постраничного вывода 197 13.05 Переадресация в С shell: сообщения об ошибках 198 13.06 Безопасная переадресация ввода-вывода 199 13.07 Порожденный shell: оператор () 200 13.08 Исполюование фигурных скобок для группирования команд в Bourne shell 201 13.09 Направление потока вывода по разным адресам 201 13.10 Как направить выходные данные нескольких команд в один файл 202 13.11 Направление стандартного вывода по различным адресам: программа tpipe 202 13.12 Отображение текста на нескольких терминалах , 203 13.13 Дефис вместо имени файла , 204 13.14 Использование файла /dev/nulj 204 13.15 Что делать с переполненной битодробилкой? :-) 205 13.16 Хранение и просмотр сообщений об ошибках 206 Часть третья. Работа с файловой системой 207 Глава 14. Перемещение по файловой системе 209 14.01 Основные способы перемещения по файловой системе 209 14.02 Относительные и полные путевые имена ; 209 14.03 Чем хорош текущий каталог , 211 14.04 Как UNIX определяет текущий каталог 212 14.05 Быстрая смена каталога: переменная cdpath 213 14.06 Команды pushd и popd 214 14.07 Удобные псевдонимы для команды pushd 216 14.08 Быстрая смена каталога с помощью пользовательских команд 216 14.09 Смена каталога по его инициалам 216 14.10 Поиск каталогов и файлов с помощью переменных 218 14.11 Быстрый поиск начального каталога любого владельца 219 14.12 Запоминание текущего каталога в переменной shell 220 14.13 Каков же мой текущий каталог? 220 14.14 Автоматическая инициализация при.входе и выходе из каталога „.„221 924
Глава 15. Метасимволы 223 15.01 Метасимволы в именах файлов 223 15.02 Основные метасимволы 224 1503 Использование оператора {} в интерпретаторах Korn shell и Bourne shell 225 15.04 Когда для метасимвола нет совпадений 226 15.05 Выбор с помощью метасимволов "скрытых" файлов 226 15.06 Метасимволы в путевых именах использовать не рекомендуется 227 15.07 Получение списка подходящих файлов с помощью команды grep -1 227 15.08 Получение с помощью команды grep -с списка файлов, не содержащих шаблона 228 15.09 Сценарий пот: список файлов, не соответствующих шаблону 229 15.10 Шаблоны для поиска каталогов 230 Глава 16. Где же эти файлы? 231 1601 Все, кроме команды find 231 16J02 Поиск старых и новых файлов 231 J6L03 Перекомпоновка списка, выведенного командой Is 233 16L04 Вывод списка содержимого всех подкаталогов 234 16L05 Временные характеристики файлов UNIX 234 16J06 Сценарии elf и els: "сжатые" списки 234 1607 Сокращения, используемые при вызове команды Is с различными опциями 236 16.08 Команда Is -d 236 ' 16.09 Команда для вывода списка последних измененных файлов 237 16.10 Сценарий findemd: поиск команды. .. 238 1611 Вывод "скрытых" файлов : 238 1612 Полезные псевдонимы для команды Is 239 16LJ3 Файл недоступен? Возможно, имя набрано с пробелами 240 16L14 Вывод непечатаемых символов в именах файлов 240 16.15 Веселый сценарий для новообращенных в UNIX 241 I6L16 Автоматическое создание уникальных имен файлов 241 16.17 Получение имени каталога из путевого имени файла 242 1618 Получение списка файлов, созданных или отредактированных сегодня 242 16.19 Сценарий stree: простое дерево каталогов. 243 1620 Программа vtree: визуализация дерева каталогов 244 1621 Поиск всех каталогов с одинаковыми именами 244 1622 Сравнение структуры двух каталогов 245 1623 Сравнение имен файлов в двух каталогах 246 1624 Подсчет файлов по типам 247 1625 Получение списка файлов в соответствии с датой последней модификации и размером 247 1626 Поиск текстовых файлов с помощью сценария findtext 248 1627 Команда newer вывод списка самых новых файлов.... 250 1628 Сценарий oldlinks: проверка целостности символических ссылок 250 1629 Команда sis: супер-команда Is с возможностью выбора формата. 251 Содержание 925
Глава 17. Команда find: поиск файлов 253 17.01 Команда find великолепна. Нужно только научиться пользоваться ею 253 17.02 Перемещение по большому дереву каталогов 255 17.03 Не забывайте добавлять оператор -print 256 17.04 Поиск файлов по шаблону 256 17.05 Поиск старых файлов 256 17.06 Станьте специалистом по операторам поиска команды find 257 17.07 Задание параметра времени в команде find 258 17.08 Поиск файлов с точно заданными временными характеристиками 259 17.09 Проблемы с оператором -newer 259 17.10 Выполнение команд в случае успешного поиска 260 17.11 Использование оператора -exec для создания пользовательских тестов 261 17.12 Поиск несвязанных объектов с помощью одной команды 262 17.13 Поиск файлов по их типу 263 17.14 Поиск файлов по размеру 263 17.15 Поиск файлов с заданными правами доступа 264 17.16 Поиск файлов по имени владельца или группы пользователей 265 17.17 Дублирование дерева каталогов 265 17.18 Использование "быстрой" команды find 265 17.19 Ускоренный поиск файлов с помощью базы данных команды find 267 17.20 Поиск файлов по их содержимому (и возникающие при этом ошибки) 269 17.21 Сценарий lookfor: поиск файлов, содержащих заданное слово 270 17.22 Поиск ссылок на файл 271 17.23 Поиск файлов с помощью оператора -prune 271 17.24 Пропуск некоторых участков дерева каталогов 273 17.25 Недопустимость поиска в сетевых файловых Системах 273 Глава 18. Созцание ссылок, переименование и копирование файлов 274 18.01 Что сложного в процессе копирования файлов 274 18.02 Что на самом деле хранится в каталоге 274 18.03 Файлы с двумя или несколькими именами 276 18.04 О ссылках 277 18.05 Создание и удаление ссылок 279 18.06 Устаревшие символические ссылки 280 18.07 Ссылки на каталоги 281 18.08 Вывод имен файлов, соответствующих символическим ссылкам 282 18.09 Переименование, копирование и сравнение групп файлов 283 18.10 Переименование можно осуществить разными способами 283 18.11 Переименование файлов с помощью команды геп 284 18.12 Переименование файлов в интерактивном режиме 286 18.13 Еще один способ переименования 286 18.14 Изменение объекта символических ссылок 287
18.15 Копирование дерева каталогов с помощью команды ср -г 287 18.16 Копирование дерева каталогов с помощью команды tar 289 Глава 19. Созцаиие и чтение архивов 291 19.01 Переезжаем на новую квартиру 291 19.02 Shell-архивы: введение 291 19.03 Программа unshar: разархивирование shell-архивов 293 19.04 Простая версия команды unshar 293 19.05 Использование программы tar для создания и распаковки архивов 294 19.06 GNU-версия программы tar 295 19.07 Извлечение файлов из сжатых архивов 296 19.08 Проблемы с диагностическими сообщениями программы tar 296 19.09 Архиватор для System V: программа cpio 297 Глава 20. Резервное копирование файлов 298 20.01 Краткое резюме по утилите tar 298 20.02 Создавайте резервные копии 299 20.03 Создание резервных копий с помощью локального ленточного накопителя 299 20.04 Восстановление файлов с ленты посредством команды tar 302 20.05 Использование команды tar для работы с удаленными ленточными накопителями 303 20.06 Запись на ленточный накопитель на удаленной машине 303 20.07 Создание файла с временными метками для выборочного резервного копирования 305 20.08 Сообщение команде tar о том, какие файлы необходимо включить или исключить 305 20.09 Когда программа не понимает метасимволов 308 20.10 Избегайте полных путевых имен при использовании команды tar 308 20.11 Располагайте аргументы команды tar в правильном порядке 309 20.12 Защита файлов с помощью SCCS и RCS 310 20.13 Использование SCCS 310 20.14 Использование RCS 311 20.15 Как получить список номеров версий RCS с помощью сценария rcsrevs 313 Глава 21. Еще о работе с файлами 314 21.01 Всякая всячина 314 21.02 Прекрасное место для хранения временных файлов: каталог /tmp 314 21.03 Уникальные имена для временных файлов 314 21.04 Зачем нужны каталоги /tmp и/usr/tmp 316 21.05 Чем полезна информация о времени последнего доступа к файлу 317 21.06 Время изменения индексного дескриптора 317 21.07 Установка времени модификации с помощью команды touch 317 21.08 Переменные MAILCHECK и mail пригодны не только для простой проверки почты 318 21.09 Автоматическое обновление отчетов по файлам с помощью утилиты make 320 21.10 Список файлов всегда отображается в верхней части экрана: сценарий dirtop 321 Содержание 927
21.11 Безопасное удаление, перемещение и копирование 322 21.12 Копирование файлов в заданный каталог.....; 323 21.13 Чтение содержимого индексного дескриптора с помощью программы stat 323 21.14 Автоматическое добавление даты к имени файла 324 Глава 22. Управление доступом к файлам 325 2201 Введение в понятия "принадлежность файла" и "защита файла" 325 22.02 Краткое руководство по правам доступа к файлам и каталогам 325 2203 Кому будет принадлежать новый файл? 328 22.04 Установка точного значения параметра команды umask 329 22.05 Права доступа для группы и бит SGID 329 22.06 Защита файлов с помощью sticky-бита 330 22.07 Изменение прав доступа к файлу с помощью команды chmod 330 2Z08 Оператор = команды chmod 332 22.09 Защита важных файлов: запрет на запись в них 332 22.10 Быстрое изменение прав доступа с помощью команд ex, cw и c-w 333 22.11 Лазейка: модификация файлов без права на запись 333 • 22.12 Каталог доступен, но список его содержимого вывести невозможно 334 22.13 Группы и групповое владение 335 22.14 Добавление пользователей в группу с целью отмены прав доступа 336 22.15 Манипулирование правами доступа 337 22.16 Копирование прав доступа с помощью утилиты cpmod 338 22.17 Способы улучшения защиты файлов, зашифрованных программой crypt 339 22.18 Очистка экрана терминала для защиты информации и предотвращения его повреждения 340 2Z19 Сценарии интерпретатора shell должны быть доступными для чтения, но не. обязательно — для выполнения - .....340 22.20 Почему невозможно изменить принадлежность файла в BSD UNIX? 341 22.21 Как изменить принадлежность файла, не используя команду chown 341 2222 Команда su — не только для суперпользователя... 342 Глава 23. Удаление файлов 345 23.01 Время создавать и время разрушать 345 23.02 Опасность команды rm 345 23.03 Способы повышения безопасности команды rm 346 23.04 На вопрос "Да или нет?" всегда отвечайте "Да" 346 23.05 Выборочное удаление 347 23.06 Более быстрый способ интерактивного удаления файлов 347 23.07 Более безопасный способ удаления файлов в некоторых каталогах 348 23.08 Безопасное удаление: за и против 349 23.09 Пакет delete: защита файлов от случайного удаления 349 23.10 Осознанное удаление: команда rm -f. 351 23.11 Удаление файлов с необычными именами , 352
23.12 Использование метасимволов для удаления файлов с необычными именами 352 23.13 Удаление файлов с "нулевым" именем 353 23.14 Работа с именем файла, начинающимся с дефиса (-).. 353 23.15 Исполюование команды unlink для удаления файлов с необычными именами 354 23.16 Удаление необычных файлов по номеру индексного дескриптора 354 23.17 Проблемы с удалением каталогов 355 23.18 Как происходит создание и удаление каталогов 356 23.19 Удаление страниц руководства, которые не читаются (BSD) 357 2320 Удаление устаревших файлов 357 23.21 Удаление всех файлов, кроме одного 358 23.22 Использование команды find для удаления ненужных файлов 359 Глава 24. Другие способы освобождения дискового пространства 360 24.01 Не удаляйте файл, а очистите его 360 24.02 Экономия дискового пространства путем направления файлов регистрации и почтовых сообщений на "битодробилку" , 361 24.03 Удаление ссылок на открытые файлы — не лучшая идея 361 ■ 24.04 Экономия дискового пространства с помощью ссылок 362 24.05 Ограничение размеров файлов 362 24.06 Экономия дискового пространства с помощью знаков табуляции.. 363 24.07 Сжатие файлов с целью экономии дискового пространства 363 24.08 Экономия дискового пространства: команда tar и сжатие дерева каталогов 364 24.09 Определение доступного дискового пространства 366 24.10 Сценарий zloop: обработка сжатых файлов 367 24.11 Редактирование сжатых файлов 368 24.12 Сжатие дерева каталогов: точная настройка 369 24.13 Экономия дискового пространства, занимаемого исполняемыми файлами: команда strip 370 24.14 Проявляйте осторожность при использовании команды strip 371 24.15 Упорядочение каталогов 371 24.16 Упорядочение гигантского каталога 372 24.17 Дисковые квоты 373 24.18 Большие файлы не всегда занимают много дискового пространства 373 Часть четвертая. Содержимое файлов 375 Глава 25. Просмотр содержимого файла 377 25.01 Коротко о содержании 377 25.02 Как поймать кошку: четыре способа 377 25.03 Использование утилиты more для постраничного просмотра файлов 378 25.04 Программа постраничного вывода less 379 25.05 Постраничный просмотр сжатых файлов, а также файлов, содержащих непечатаемые символы 380 25.06 Что стоит за пробелами .. 380 "■ ■ pi щ*1 ' ' ' Содержание 929
-25.07 Отображение непечатаемых символов с помощью команд cat -v и od -с 381 25.08 Определение типа файла 383 ' 25.09 Добавление и удаление пробельных символов 384 25.10 Сжатие пустых строк 384 25.11 Сценарий crush — аналог команды cat 385 25.12 Пустые строки: удваивание, утраивание 385 25.13 Сценарий pushin: удаление лишних пробелов 386 25.14 Как просмотреть конец файла: команда tail 386 25.15 Управление командой tail 387 25.16 Как проследить за появлением новых строк в файле 387 25.17 Замена для команды tail 388 25.18 Наблюдение за увеличением объема нескольких файлов 388 25.19 Обратный порядок следования строк: программа flip 389 25.20 Вывод начала файла 389 25.21 Нумерация строк 390 Глава 26. Регулярные выражения (шаблоны) 391 26.01 Это выражение 391 26.02 Не путайте регулярные выражения с метасимволами 391 26.03 Как понимать выражения 392 2604 Использование метасимволов в регулярных выражениях 394 2605 Как создать правильное регулярное выражение 399 2606 Чему соответствует регулярное выражение 400 2607 Ограничение размера совпадающей строки 402 2608 Примеры регулярных выражений 402 2609 Метасимволы, действительные для различных UNIX-программ 403 2610 Краткий справочник по шаблонам 404 Глава 27. Поиск в файлах 408 27.01 Различные версии команды grep 408 27.02 Поиск текста с помощью команды grep 409 27.03 Поиск несовпадающего текста 409 27.04 Поиск слов 410 27.05 Расширенный поиск текста с помощью команды egrep 410 27.06 Быстрая команда fgrep на самом деле таковой не является 411 27.07 Поиск с применением нескольких шаблонов 412 27.08 Система glimpse и команда agrep 412 27.09 Новые команды grep намного быстрее 414 27.10 Поиск в RCS-файлах с помощью сценария rcsgrep 414 27.11 Использование редактора sed для многострочного поиска 415 27.12 Создание пользовательских команд в стиле grep с помощью языка Perl 416 27.13 Еще несколько программ типа grep, написанных на Perl 417 27.14 Комбинированный поиск 418
27.15 Сужение диапазона поиска 419 27.16 Поиск, осуществляемый без учета регистра 419 27.17 Поиск символа, находящегося в определенной позиции 420 27.18 Быстрый поиск и проверка правописания с помощью программы look 420 27.19 Поиск слов в двоичных файлах 420 27.20 Команда grep, выделяющая найденные слова 421 Глава 28. Сравнение файлов 422 28.01 Установление различий с помощью команды diff 422 28.02 Сравнение трех версий файлов с помощью команды dirD 423 28.03 Вывод контекста командой diff 424 28.04 Два файла рядом: команда sdiff. 426 28.05 Два файла рядом: сравнение и взаимное смещение 427 28.06 Отбор вариантов с помощью команды sdiff 427 28.07 Сравнение очень длинных файлов: команда bdiff 427 28.08 Более дружественный вывод команды diff 428 28.09 Сценарии для редактора ех, создаваемые командой diff 429 28.10 Проблемы, связанные с командой diff и позициями табуляции 431 28.11 Программы стр и diff 431 28.12 Сравнение двух файлов с помощью команды сотт 431 28.13 Программа make — не только для программистов 433 28.14 Другие способы использования программы make 435 28.15 Отображение изменений в troff-файле с помощью команды diffmk 436 Глава 29. Проверка правописания, поцсчет слов и анализ текста 437 29.01 Команда spell 437 29.02 Интерактивная проверка правописания с помощью программы ispell 438 29.03 Проверка правописания слова 439 29.04 Работа команды spell 440 29.05 Добавление слов в словарь программы ispell 441 29.06 Подсчет строк, слов и символов 443 29.07 Подсчет количества повторений слов 445 29.08 Поиск удвоенных слов 445 29.09 Поиск закрывающих скобок 446 29.10 Только слова, пожалуйста 447 Часть пятая. Рецактирование текста 449 Глава 30. Особенности редактора vi 451 30.01 Редакторы vi и ех: зачем так много информации? 451 30.02 О чем пойдет речь 451 30.03 Мыши против редактора vi 452 30.04 Редактирование нескольких файлов 453 Содержание 931
30.05 Вставка текста из одного файла в другой... 454 30.06 Локальные установки для редакторов vi и ех 455 30.07 Использование буферов для перемещения и копирования текста 456 30.08 Восстановление удаленных данных из пронумерованных буферов 457 3009 Поиск по шаблону и глобальные команды 457 30.10 Подтверждение замены в редакторах ех и vi 458 ЗОИ Сохраняйте первоначальный вариант файла и записывайте изменения в новый 459 30.12 Сохранение части файла _ 459 30.13 Добавление текста в конец существующего файла 460 3014 Перемещение блоков текста, заданных шаблонами 460 3015 Популярные команды глобальных изменений 461 3016 Подсчет количества слов, отключение повторного поиска 462 30.17 Преобразование начальных букв каждого слова строки в прописные 462 30.18 Автоматическая установка опций редактора vi для отдельных файлов 463 30.19 Строки режима: ошибка или полезное свойство? _ 463 30.20 Использование нескольких файлов установок; поиск при запуске 464 3021 Отдельный набор установок для каждого файла 465 3022 Фильтрация текста с помощью UNIX-команд - 465 3023 Безопасные фильтры редактора vi 467 3024 Восстановление файлов в редакторах vi и ех и сетевые файловые системы 468 3025 Команда vi -г и сохранение.файла после выхода из редактора 469 3026 Временный выход в интерпретатор shell _ 469 3027 Комбинированный поиск в редакторе vi 470 3028 Отслеживание функций и включаемых файлов с помощью команды ctags и файла tags. 472 30.29 Задание нескольких файлов tags 473 30.30 Проблемы с клавишей Esc в редакторе vi 473 30.31 Режим автозамены в редакторе vi 473 3032 Использование режима автозамены для обмена файлами.. „ 474 3033 Автозамена часто встречающихся опечаток... 475 30.34 Команды работы со строками и символами в редакторе vi 475 30.35 При нехватке памяти для временных файлов воспользуйтесь другим каталогом 476 30.36 Открытый режим редактора ех может оказаться удобным 477 30.37 Выравнивание текста 479 30.38 Как найти предыдущее место редактирования с помощью команды undo 480 Глава 31. Создание пользовательских команд в редакторе vi 481 3L01 Не вводите лишнего. _..._ 481 3L02 Экономия времени с помощью команды тар 481 3L03 Что вы теряете при использовании команды тар! 484 3104 ©-Функции 484 3L05 Переназначения клавиш для вставки текста в окно, в котором запущен редактор vi 486 31.06 Защита клавиш от их интерпретации редактором ех 487 3L07 Переназначения для повторяющихся команд редактирования. 488 932
31J08 Другие примеры переназначения клавиш в редакторе vi 489 31.09 Полезные установки для файла .ехгс 490 3L10 Переназначение повторяющихся нажатий клавиш 494 31.11 Набор текста прописными буквами без переключения клавиши [CAPS LOCK] 494 3L12 Перемещение курсора в режиме ввода текста без использования клавиш со стрелками . 495 3L13 Управление курсором в режиме ввода текста 496 31.14 Не теряйте важных функций из-за переназначений клавиш — пользуйтесь опцией noremap 497 31.15 Как обойти ограничения редактора vi и заставить его выполнять сложные макросы 497 31.16 Макросы редактора vi для разбиения длинных строк 498 Глава 32. GNU Emacs 499 32.01 Emacs — еше один редактор 499 3202 Особенности редактора Emacs 499 32.03 Параметры и способы их изменения 501 32.04 Резервные файлы и файлы автосохранения 501 32J)5 Перевод редактора Emacs в режим замены 502 32Л6 Дополнение команд 503 32.07 Любимые методы для экономии времени :...:... „ 503 32.08 Рациональный поиск '.'. 504 32.09 Сброс переменной среды PWD перед использованием редактора Emacs 505 32.10 Вставка в файлы управляющих символов :...:. 505 32.11 Использование режима автозамены 506 32.12 Решение проблемы управления потоками 507 32.13 Как убить свободное время 508 Глава 33. Пакетное редактирование 509 33.01 Почему строковые редакторы до сих пор применяются....; 509 33.02 Создание сценариев редактирования 509 33.03 Адресация строк 510 33.04 Полезные команды редактора ех 511 33.05 Выполнение сценариев редактирования из редактора vi 514 33.06 Изменение нескольких похожих файлов 514 33.07 Пакетное редактирование: как избежать ошибок, когда нет соответствия шаблону 515 33.08 Ошибки пакетного редактирования при обработке больших файлов.. 516 33.09 Программа patch: обновление файлов, обработанных командой diff 516 33.10 Быстрая глобальная замена из командной строки с помощью программы qsubst - 518 33.11 Краткий справочник: утилита awk ——.—..518 33.12 Версии утилиты awk _ 526 Глава 34. Потоковый редактор sed 527 34.01 Два важных момента, касающихся редактора sed 527 34.02 Вызов редактора sed 527 Содержание 933
34.03 Тестирование сценариев: программы checksed и runsed 528 34.04 Основы адресации 529 34.05 Порядок команд в сценарии 531 34.06 Пошаговое выполнение команд 532 34.07 Выбор разделителей для регулярного выражения 532 34.08 Символы новой строки в строках замещения 533 34.09 Ссылка на строку поиска в строке замещения 533 34.10 Ссылка на отдельные части строки поиска 534 34.11 Поиск и замещение: одно соответствие шаблону из нескольких возможных 535 34.12 Преобразование текста 535 34.13 Область хранения: временный буфер 535 34.14 Преобразование части строки 537 34.15 Редактирование с переходом через границы строк 539 34.16 Осмотрительный писарь 541 34.17 Поиск шаблонов, расположенных в разных строках 543 34.18 Удаление нескольких строк : 545 34.19 Выполнение редактирования в любом месте, кроме 546 34.20 Команда проверки 547 34.21 Команда выхода 547 34.22 Опасности, связанные с использованием команды выхода 548 34.23 Символы новой строки и обратная косая черта в сценариях интерпретатора shell 548 34.24 Краткий справочник по редактору sed 549 Глава 35. Альтернативные способы редактирования 557 35.01 А почему, собственно, альтернативные? 557 35.02 Обработка текста с помощью утилиты fmt 557 35.03 Альтернативы утилите fmt 558 35.04 Сценарий recomment: форматирование блоков комментариев программы 559 35.05 Удаление заголовков в почтовых сообщениях с помощью сценария behead 560 35.06 Низкоуровневая обработка файлов с помощью утилиты dd 561 35.07 Сценарий offset: отступы в тексте 561 35.08 Сценарий center центрирование строк файла 563 35.09 Разбиение файлов в фиксированных точках: утилита split 563 35.10 Разбиение файлов с учетом контекста: утилита csplit 565 35.11 Обработка символов с помощью утилиты tr 567 35.12 Преобразование текста из кодировки EBCDIC в кодировку ASCII 569 35.13 Другие варианты преобразования с помощью утилиты dd "570 35.14 Вырезание колонок текста или полей с помощью утилиты cut 570 35.15 Вырезание колонок с помощью утилиты colrm 571 35.16 Автоматическое форматирование колонок с помощью утилиты cols 571 35.17 Форматирование колонок текста с помощью утилиты рг 572 35.18 Вывод данных в несколько колонок 574 35.29 Объединение строк с помощью утилиты join 574
3520 Краткий справочник: утилита uniq 575 35.21 Использование переменной IFS для разбиения строк 576 35.22 Выравнивание колонок 577 3523 "Вращение" текста 578 Глава 36. Сортировка 579 36.01 О чем пойдет речь в данной главе 579 36.02 Поля для сортировки: как работает утилита sort 579 36.03 Изменение разделителя полей 581 36.04 Путаница из-за разделителей полей — пробельных символов 581 36.05 Алфавитный и числовой принцип сортировки 583 36.06 Различные советы относительно операции сортировки 584 36.07 Сортировка элементов, состоящих из нескольких строк 585 36.08 Сценарий lensort: сортировка строк по их длине 587 36.09 Сортировка списка людей по фамилиям 587 Глава 37. Язык Perl 588 37.01 Что мы расскажем и чего не станем рассказывать о языке Perl 588 37.02 Зачем нужно изучать Perl (часть 1) 588 37.03 Три добродетели программиста 590 37.04 Зачем нужно изучать Perl (часть 2) 590 37.05 А теперь поговорим о Perl 5 593 Часть шестая. Управление процессами 595 Глава 38. Запуск, останов и уничтожение процессов 597 38.01 О чем рассказывается в этой главе 597 38.02 Системные вызовы fork и exec 597 38.03 Управление процессами: общие сведения 598 38.04 Порожденные интерпретаторы shell 600 38.05 Команда ps 601 38.06 Управляющий терминал 603 38.07 Почему команда ps выводит некоторые команды в скобках 604 38.08 Что такое сигналы? 604 38.09 Уничтожение интерактивных заданий 606 38.10 Уничтожение процессов по команде kill 606 38.11 Наблюдение за очередью на печать: перезапускаемый сценарий-демон интерпретатора shell 607 38.12 Уничтожение всех процессов пользователя 609 38.13 Интерактивное уничтожение процессов, соответствующих шаблону 610 38.14 Процессы стали неуправляемыми? Пошлите им сигнал STOP 611 38.15 Удаление "неуничтожаемых" процессов 611 38.16 Почему невозможно уничтожить процессы-зомби? «,..612 Содержание 935
38.17 Автоматическое уничтожение фоновых процессов интерпретатором csh при выходе из системы 612 38.18 Команда nohup 613 Глава 39. Время и производительность 614 39.01 Поговорим о времени 614 39.02 Программы хронометрирования 614 39.03 Переменная time интерпретатора С shell 615 39.04 Определение среднего времени выполнения команды с помощью сценария runtime 617 39.05 Почему система работает так медленно? 618 39.06 Команда lastcomm: какие команды сейчас выполняются и как долго они будут выполняться? 620 39.07 Проверка загруженности системы: команда uptime 621 39.08 Замедление работы программы в "раздутой" среде 622 39.09 Знай, когда нужно быть уступчивым по отношению к другим пользователям, а когда нет.. 622 39.10 Ошибка при работе с командой nice 625 39.11 Изменение приоритета задания в BSD UNIX 625 39.12 Почему компьютер работает медленно и как это исправить 626 Глава 40. Выполнение заданий по расписанию 630 40.01 Выполнение заданий в свободное время 630 40.02 Если нужно немного подождать: команда sleep 630 40.03 Команда at 631 40.04 Выбор интерпретатора shell с помощью команды at 632 40J05 Как избежать накладок при выполнении at- и сгоп-заданий 633 40.06 Пакетная обработка заданий в System V.4 633 40.07 Как избавиться от вывода at-заданий 634 40.08 Автоматический повторный запуск at-заданий 635 40.09 Проверка и удаление задании 635 40.10 Сценарии nextday и nextweekday: завтра и на следующий будний день 636 40.11 Посылайте себе письма с напоминаниями 637 40.12 Периодическое выполнение задании: программа сгоп 637 40.13 Добавление новых crontab-элементов 640 40.14 Включение стандартного ввода в crontab-элементы 640 40.15 Сценарий crontab упрощает редактирование crontab-элементов и делает его более безопасным .' 641 Часть седьмая. Терминалы и принтеры 643 Глава 41. Настройка терминала и последовательного порта 645 4L01 О чем пойдет речь в главе 645 41.02 Команда stty и материал, с которым она работает 646 4L03 Определение установок терминала с помощью команды stty 1651 4L04 Как UNIX обрабатывает символы табуляции 651 936
42.05 Почему в некоторых системах курсор можно свободно перемещать по символам приглашения 653 41.06 Использование команды sleep для защиты установок порта от изменения 654 41.07 Чтение с терминала оч-ч-чень длинных строк 655 41.08 Псевдотерминалы и оконные системы 655 41.09 Команды для настройки терминала 656 41.10 Использование возможностей базы данных terminfo в программах интерпретатора shell.657 41.11 Описания терминалов в базах данных termcap и terminfo 658 41.12 Определение символов, которые генерируются специальными клавишами терминала 662 Глава 42. Проблемы с терминалами 664 42.01 Механизм вывода на экран терминала 664 42.02 Как восстановить терминал или задание после зависания 666 42.03 Почему изменение переменной TERM иногда оказывается безрезультатным 668 42.04 Восстановление исходного состояния терминала с нарушенными установками 668 42.05 Как восстановить размеры экрана 671 42J06 Файлы для проверки размеров экрана 673 42.07 Сценарий termtest: выдача на терминал повторяющихся символов 675 42.08 Сообщения об ошибках исчезают слишком быстро? 676 Глава 43. Печать 678 43.01 Немного истории 678 43.02 Основные команды вывода на печать 679 43.03 Управление принтером с помощью команды 1рс ; 681 43.04 Использование различных принтеров 682 43.05 Использование символических ссылок при печати /".., 683 43.06 Печать на терминальном принтере 683 43.07 Быстрое форматирование перед печатью , 684 43.08 Установка полей с помощью команд рг и fold ,.,....! .^685 43.09 Отступы при печати ,....../' /... 685 43.10 Вывод имени файла в начале распечатки ;...../. <'.. 686 43.11 Крупные буквы: команда banner ./..../..../. ..'. 687 43.12 Типографский набор .„./....,.. 688 43.13 Средства форматирования текста: nroff, troff, ditroff ... /....'. /.' 690 43.14 Программы nroff/troff и пакеты макросов ./. ,-. 691 43.25 От исходного файла к принтеру ...........................^......l '. 691 43.16 Программа groff. ..../. /. 693 43.17 Не имеете программы nroff? Попробуйте gnroff или' awf ■•/• 693 43.18 Как программа nroff создает полужирный и подчеркнутый шрифты и как их удалить.... 694 43.19 Удаление начальных символов табуляции и прочих мелочей 695 4320 Вывод макроопределений программы troff. 696 4321 Предварительная обработка ввода программы troff с помощью редактора sed 697 4322 Преобразование текстовых файлов в формат PostScript 698 Содержание 937
43.23 Программа psselect: печать отдельных страниц из PostScript-файла 699 43.24 Другие PostScript-утилиты 700 4325 Пакет создания переносимой графики 701 Часть восьмая. Программирование в среде shell 707 Глава 44. Программирование для начинающих 709 44.01 Каждый должен иметь навыки программирования в shell 709 44.02 Создание простого сценария 710 44.03 Что же такое shell? 712 44.04 Проверка механизма выполнения сценариев 713 44.05 Проверка условий с помощью оператора case интерпретатора Bourne shell 714 44.06 Шаблоны оператора case 715 44.07 Код завершения процесса 715 44.08 Проверка кода завершения с помощью оператора if 716 44.09 Проверка успешности выполнения команды 717 44.10 Циклы, в которых проверяется код завершения 718 44.2J Установка кода завершения сценария 719 44.12 Обработка сигналов прерывания команд 719 44.13 Команда read: чтение данных с клавиатуры 721 44.14 Включение в сценарий команд awk, sed и др 721 44.J5 Обработка аргументов командной строки в сценариях 722 44.16 Обработка аргументов командной строки с помощью цикла for 723 44.J7 Обработка аргументов командной строки с помощью цикла while и команды shift 725 44.18 Стандартный анализ командной строки 726 44.J9 Команда set интерпретатора Bourne shell 728 44.20 Команда test: проверка файлов и строковых переменных 729 44.21 Выбор имени новой команды 730 4422 Определение имени программы и множественные имена программ , 730 4423 Чтение файлов с помощью команд . и source 731 Глава 45. Программирование для подготовленных пользователей 732 45.01 Для тех, кто знаком с основами 732 45.02 Сказка об операторах :, # и #! 734 45.03 Для выполнения вашего сценария не нужен интерпретатор shell? He запускайте его 734 45.04 Поиграем с #! 735 45.05 Файл, который сам показывает свое содержимое, или что делает оператор #! 736 45.06 Обеспечение работы сценария без интерпретатора Bourne shell 737 45J07 Команда exec 737 45.08 Обработка сигналов для порожденных процессов 738 45.09 Бесценный оператор : в Bourne shell 739 45.10 Удаление открытого файла в целях безопасности и упрощения очистки диска 740 45.11 Многоцелевая команда jot 741 938
45.12 Подстановка параметров 744 45.23 Экономия дискового пространства и времени программирования: присвоение программе нескольких имен 745 45.14 Определение значения последнего аргумента командной строки 746 45.25 Как отменить установку всех параметров командной строки 746 45.26 Стандартный ввод в цикле for 747 45.17 Цикл for с несколькими переменными 747 45.18 Использование команд basename и dimame 748 45.29 Цикл while с несколькими командами управления циклом 749 45.20 Открытие файлов и дескрипторы: обзор 749 45.21 Оператор п>&ш: переадресация стандартных потоков вывода и ошибок 751 4522 Построчная обработка файлов 754 45.23 Ввод и вывод для циклов с переадресацией ввода-вывода 757 45.24 Интерпретатор shell может читать Сценарий со стандартного ввода, но 758 4Л25 Сценарии, получаемые "на лету" со стандартного ввода 758 45.26 Признак конца файла в конструкции "документ здесь" 759 45.27 Выключение эха при вводе "секретных" ответов 760 45.25 Краткий справочник по команде ехрг 760 45.29 Проверка символов в строке с помощью команды ехрг 762 45.30 Выделение частей строк 762 45.32 Вложенная подстановка результатов выполнения команды 764 45.32 Улучшенная команда read: grabchars 765 45.33 Проверка двух строк с помощью одного оператора case 766 45.34 Массивы в интерпретаторе Bourne shell 767 45.35 Использование в сценарии управляющих символов 767 45.36 Файл блокировки в интерпретаторе shell 768 Глава 46. Отладка сценариев shell 770 46.01 Советы по отладке сценариев 770 4602 Проблемы с защитой специальных символов? Подумайте, а затем запустите команду echo 771 46.03 Вывод значений переменных в интерпретаторе Bourne shell 772 46.04 Предотвращение синтаксических ошибок при сравнении чисел 772 46.05 Предотвращение синтаксических ошибок при сравнении строковых переменных 773 4606 Ошибка при вызове интерпретатора Bourne shell с опцией -е 773 46.07 Защита специальных символов и аргументы командной строки 774 46.08 Проверка встроенных команд 775 46.09 Если команда не возвращает код завершения, проверяйте сообщения об ошибках 776 4610 Переносимая команда echo 777 Глава 47. Программировать для С shell? Нет! 778 47.01 Почему нет? 778 47.02 Пагубность программирования для С shell 778 Содержание 939
47.03 Условные конструкции, с оператором if..; 784 47.04 Переменные, операторы и выражения в интерпретаторе С shell 785 47.05 Использование массивов интерпретатора С shell 787 47.06 Оператор switch интерпретатора С shell: краткое описание ; 788 Часть девятая. Разное 789^ Глава 48. Автоматизация офиса 791 48.01 О чем пойдет речь в этой главе 791 48.02 Интерактивные списки телефонов и адресов 791 48.03 Черновик на экране 792 4804 Планирование работ: программа calendar. 793 48.05 Команда leave: настойчивые напоминания о том, что пора уходить 795 48.06 Получение календаря на любой месяц любого года: программа cal 796 48.07 Команда cal, выделяющая сегодняшнее число 796 48.08 Календарь для терминалов и принтеров с длиной строки в. 132 символа 797 48.09 Календари в формате PostScript, созданные с помощью программы peal 798 48.10 Работа с фамилиями и адресами .' 800 4811 Программа index и работа с базами данных :.... 805 48.12 Использование программы index с фильтром 807 Глава 49. Работа с числами 808 49.01 Программа be: простые математические операции в командной строке 808 49.02 Программа be: преобразование чисел в шестнадцатеричный и двоичный'формат 809 49.03 Проблемы при преобразовании чисел из одной системы счисления в другую 809 49.04 Аргументы функций синуса и косинуса должны задаваться в радианах 810 49.05 Преобразование чисел из одной системы счисления в другую с помощью программы cvtbase ...„ 810 49.06 Команда ехрп простые арифметические операции...... 811 49.07 Сценарий addup: определение итогового значения в столбце 811 49.08 Электронные таблицы — это здорово 812 49.09 Создание деловой графики с помощью программы ipl 813 Глава 50. Справочная система 814 50.01 Интерактивная документация UNIX 814 50.02 Команда apropos 815 50.03 Имитация команды apropos в системах, где ее нет 815 50.04 Команда whatis: однострочное описание команды 817 50.05 Команда whereis: определение местоположения файла 817 50.06 Просмотр разделов интерактивной документации 818 50.07 Как UNIX-система определяет свое имя в сети 819 50.08 С какой версией я работаю? 819 50.09 Чтение предметного указателя с перестановкой ключевых слов 821 940
50.10 Как создать интерактивную документацию, не изучая программу troff 822 50.11 Написание простой man-етраницы с помощыо макроса -man 823 50.12 Типичные сообщения об ошибках в UNDC 825 Глава 51. Полезные программы и забавные случаи 828 51.01 Несколько программ под занавес 828 51.02 Как UNIX вычисляет текущее время 828 51.03 ASCII-символы и их коды 829 51Л4 Кто в системе? 830 51.05 Регистрация выполняемых команд с помощыо программы script 830 5106 "Чистка" файла сценария 831 5107 Когда не хватает терпения 832 5108 Вводите выстрел и звездочку и не забывайте о кроличьих ушках 832 5109 Создание регистрационного интерпретатора shell 833 51.10 Программа date 834 51.11 Создание файла произвольного размера для тестирования 834 5L12 Вам не хватает улыбок?.......: 834 Глава 52. Что находится на компакт-диске? 836 5Z01 Введение ......:.... :.... 836 52.02 Свободно распространяемые программы для UNEX 837 52.03 Коммерческое программное обеспечение для UNIX 837 52.04 Краткое описание содержимого компакт-диска 838 52J05 Работа с прилагаемым компакт-диском .....: 854 5Z06 У вас нет дисковода компакт-дисков? : 864 52.07 Другие способы получения программного обеспечения 864 52М8 Построение программ из исходных текстов 867 5209 Поддержка программного обеспечения от компании RTR ....878 Глава 53. Словарь терминов 879 Предметный указатель 889 Содержание 941
Издательская группа BHV и издательство /УИрина" предлагают Р.Петерсен Linux: полное руководство издание 2-е, дополненное, в 2-х томах, с компакт-диском В книге даны рекомендации по применению Linux в качестве эффективного средства взаимодействия с Internet, по использованию ее UNIX-возможностей и по системному администрированию. Пользуясь прилагаемым к книге компакт-диском, вы сможете инсталлировать на своем персональном компьютере одну из последних версий Linux и многие ее приложения, а также один или несколько серверов Internet (включая ftp- и Web-серверы). Т. Чан Системное программирование иа C++ для UNIX 592 с. В данной книге содержится обзор всех современных технологий, используемых при создании сложных системных приложений для среды UNIX. Уделяется внимание реальным проблемам, с которыми сталкиваются разработчики сетевых приложений и приложений клиент/сервер, баз данных, компиляторов, операционных систем и приложений САПР. Вы узнаете, как написать на C++ такие программы, которые были бы компактными, легко сопровождаемыми и переносимыми на большинство UNIX- и POSIX-совместимых систем (например, Windows NT). К.Пэтчетт, М.Райт CGI/Perl: создание программ для Web 624 с, с компакт-диском Изложены профессиональные приемы CGI-программирования и оригинальные подходы к решению типичных CGI-задач, подробно описаны новые программы и подпрограммы, методы обработки ошибок и передачи параметров, а также некоторые детали программирования, необходимые при инсталляции и использовании CGI-программ на Web-узле. Представлено почти 10000 строк отличного кода, который многому научит читателя. Р. Шварц, Т. Кристиансен Изучаем Perl 320 с. В книге излагаются основы программирования на языке Perl, который стал стандартным для большинства UNIX-платформ и все чаще используется в среде Windows. В США книга стала бестселлером и приобрела статус учебника, предназначенного как для занятий с преподавателем, так и для самостоятельной работы. В данном издании рассматривается современная версия Perl 5.004. В каждой главе даются упражнения, а в конце книги — их подробные решения. Приведено множество примеров небольших программ, дано введение в CGI-программирование для Web, изложены методики использования системных команд в Perl-программах, рассмотрены способы создания с помощью Perl баз данных DBM и другие вопросы.
Официальные представители Издательской группы BHV: Россия Украина Фирма "Спаррк" Издательство "Ирина Тел./факс: (095) 196-07-19, 196-08-28, Тел./факс: (044) 269-04-23, 269-25-78 929-17-52 Email: sparrk@dtyline.ru Email: market@bhv.kiev.ua Ищем региональных представителей по распространению книг в Украине и России С предложениями обращаться: Фирма "Спаррк" Издательство "Ирина" Пишите нам по адресу: pg@bhv.kiev.ua
Учебное пособие Джерри Пик, Тим О'Райли, Майк Лукидис UNIX инструментальные средства Редакторы ИВ. Карпышенко, Т.Ф. Мартынюк Технический редактор З.В. Лобач Оригинал-макет подготовлен "Издательской группой BHV". Р.с. № 24267104 от 20.06.96. 252032, г. Киев, ул. Старовокзальная, 13 Подписано в печать 16.06.99. Формат 84х 108 1/16. Печать высокая. Усл. печ. л. 59. Тираж 3000 экз. Заказ № 9-171. Отпечатано в AT "Киевская книжная фабрика". 252054, г. Киев, ул. Воровского, 24
UNIX/ОПЕРАЦИОННЫЕ СИСТЕМЫ UHlXmimmm щст По своей природе UNIX шляется средой "мощных инструментальных средств". Даже начинающие пользователи UNIX быстро замечают, какая мощь заключена в возможностях shell-программирования и псевдонимах, механизме управления перечнем введенных команд и различных средствах редактирования. Однако лишь немногие способны в полной мере использовать всю эту мощь, поскольку для этого слишком многое необходимо выучить и узнать. Если вы уже работали в UNIX, то наверняка знаете, что многие программы доступны в Internet. Поэтому на компакт-диск, прилагаемый к данной книге, мы поместили лишь то, что, по нашему мнению, является лучшим бесплатным программным обеспечением для пользователей UNIX. Диск содержит тысячи советов, сценариев и полезных утилит, которые помогут сделать работу в UNIX более легкой, эффективной и даже, в некотором смысле, забавной. Помимо этого, на компакт-диске записаны мощные бесплатные программы, которые значительно расширяют возможности стандартного набора команд UNIX. Все сценарии и программы, описанные в книге, есть на компакт- диске, и вы можете смело помещать их в собственный набор системных инструментальных средств. Более того, если вы работаете на одной из распространенных UNIX-платформ, вам даже не придется компилировать программы, так как они уже скомпилированы для платформ Solaris, SunOS. Digital UNIX, IBM AIX, HP/UX, Linux, Intel и SCO UNIX (Двоичные файлы для SCO UNIX наверняка можно будет запустить на большинстве UNIX-платформ Intel.) Компакт-диск записан в формате ISO-9660. Он может быть смонтирован как обычная файловая система UNIX и просмотрен с помощью стандартных команд (для использования программ их необходимо инсталлировать на жесткий диск с помощью специальной программы инсталляции, которую мы также предоставляем). В данном издании собраны советы экспертов UNIX, взятые из книг по указанной тематике серии "Nutshell Handbook" издательства O'Reilly и заметок в Usenet, а также материал, специально написанный для этой книги различными авторами, среди которых Джерри Пик, Тим О'Райли, Дэйл Дохерти, Майк Лукидис, Крис Торек, Брюс Барнетг, Джонатан Кейменс. Джин Спаффорд, Симеон Гарфинкель и другие. И все это организовано в необычном "гипергекстоюм"' формате. В книге рассмотрено множество утилит POSIX, включая GNU-версии. Подробно описаны интерпретаторы hash и tcsh, но основное внимание акиентируегся на ключевых концепциях интерпретаторов sh и csh знание которых позволит вам одинаково успешно работать со всеми интерпретаторами UNIX. ISBN 966-552-020-2 O'REILLY8 7ROAA^ ^0П9П7