принятую в nroff для начала страницы. Эта команда nroff прогоняет страницу и увеличивает на единицу счетчик страниц. Возможно, вы не хотите менять нажатие клавиш control-L на это значение, но вы всегда можете указать в командном файле такие действия, какие вам нуж- ны. ПРИМЕРЫ 1. $ stripf module1.c | grep >"^\.bp$" | wc -l Печатает число модулей-функций, содержащихся в файле module1.c, путем поиска каждого появления команд новой страницы программы nroff и их подсчета. Мы знаем, что одна из таких команд является выходом для каждой обнаруженной функции. Данную строку можно вложить в предложение echo, которое говорит "имеется X модулей в файле $FILE". 2. $ for FILE in *.c ../*.c $HOME/src/*.c > do > stripf $FILE > done >> /tmp/func.hdrs Данный цикл for распространяется на все файлы в текущем каталоге, которые оканчиваются на .c, все файлы в родительском каталоге с таким же суффиксом и все файлы в подкаталоге src моего home-каталога с тем же суффиксом. Из каждого файла извлекается документация о функциях и нап- равляется в стандартный вывод. Весь цикл перенаправлен с помощью >>, поэтому выход каждого вызова stripf ДОБАВЛЯЕТСЯ к файлу в /tmp. ПОЯСНЕНИЯ Вся программа - это один большой цикл for в строках 4-19. Этот цикл присваивает переменной FILE каждое имя, имеющееся в командной строке. Данный командный файл не имеет опций и обработки ошибок. Команда sed системы UNIX вызывается для каждого имени файла. Прог- рамма sed читает весь вход и выводит измененный текст в стандартный вы- вод. Опция -n используется в sed для подавления всего вывода, в проти- воположность действию по умолчанию, когда все печатается. Мы используем этот флаг по той причине, что мы хотим указать программе sed, когда пе- чатать выход. Опция -e применяется, чтобы сообщить программе sed, что следующая последовательность текста между одинарными кавычками является выражением, которое нужно вычислить. Напомним, что sed - потоковый редактор, который читает одну стро- ку, сверяет ее с выражениями, затем читает следующую строку и делает все сначала. Первое, что мы ищем - символ control-L, стоящий в строке самостоятельно. Если мы не находим его, проверяется следующая строка и так далее, пока не будет обнаружен control-L. (Еще раз напомним, что вместо обозначения ^L в коде должен быть введен настоящий control-L.) Когда обнаружен control-L, он активизирует все выражение, заклю- ченное в фигурные скобки. Первым действием является подстановка команды начала страницы программы nroff, как описано ранее. Эта подстановка пе- чатается, что является самым первым выводом. В строке 9 объявлена метка "loop". Это не приводит ни к каким действиям, но устанавливает точку перехода, которая впоследствии используется. (Управляющие структуры программы sed довольно примитивны, но они позволяют описать выполняемую работу.) Строка 8 использует команду n программы sed, чтобы вызвать чтение следующей строки. Мы разобрались с первой строкой - строкой, которая содержит control-L - так что мы можем ее отбросить. В случае выполнения цикла мы видим, что sed продвигается по нашему требованию, но не прод- вигается сам. Вспомним модель документации, рассмотренную ранее. Эта модель включает документирующий заголовок для файла в целом, выполненный в обычном стиле языка Си. Модель завершается символом control-L. Этот первый блок обрабатывается с помощью stripc, как описано ранее. Мы не хотим использовать его здесь при работе со stripf. Поэтому мы сейчас должны спозиционироваться после файлового документирующего заголовка. Вслед за символом control-L имеется еще один набор из одной или более строк комментария языка Си, которые описывают функцию, следующую за ними. Далее идет само имя функции, объявление параметров и открываю- щий символ самой функции, которым является левая фигурная скобка (}). Строка 11 ищет эту фигурную скобку. Если она найдена, выполнение переходит на метку exit (строка 14). Мы можем полагать, что мы все сде- лали, если найдена левая фигурная скобка, так как этот символ должен появляться только в начале функциимодуля. Когда мы находим фигурную скобку, мы уже напечатали к этому моменту всю комментирующую информацию и заголовок функции посредством строки 12, которую мы сейчас опишем. А что если фигурная скобка появляется в поле комментария? Нет проблем, поскольку поиск фигурной скобки привязан к началу строки с помощью сим- вола ^. Он производит выражение, означающее "от первого символа в стро- ке". Мы только тогда сопоставляем фигурную скобку этому выражению, ког- да она встречается в качестве самого первого символа в строке. Затем строка 12 предполагает, что мы еще не обнаружили фигурную скобку и поэтому мы должны напечатать строку. Оператор p печатает теку- щую строку, которую обрабатывает sed. Этот вывод направляется на экран. Строка 13 - безусловный переход на метку loop. Отметим, что этот переход только изменил процесс выполнения и не привел к чтению еще од- ной вводной записи. С этим мы должны быть осторожны при управлении про- цессом выполнения в программе sed. Мы только что напечатали текущую за- пись, поэтому теперь мы должны отбросить ее и получить следующую за- пись, что означает возврат в строку 9. Этот цикл печати и чтения следу- ющей записи продолжается до обнаружения фигурной скобки, которая пере- водит выполнение на метку exit. Строка 14 - это метка exit. Когда мы попадаем на нее, мы знаем, что был обнаружен control-L, напечатан комментирующий заголовок, напе- чатаны имя функции и объявления ее параметров и найдена фигурная скоб- ка. Заметим, что фигурная скобка еще не напечатана. Когда мы находим ее, мы только делаем ветвление. Строка 15 завершает вывод, вставляя некоторый текст в выводной по- ток. Мы не можем сказать "печатать это в буквенном виде", поэтому про- исходит движение вправо по тексту, как по команде echo. Мы должны сыг- рать на правилах, установленных программой sed. Любой вывод должен быть порожден обычными командами в стиле редактора ed. Вставка текста с по- мощью команды "i" делает нам это. Отметим, что мы также вставляем сим- вол возврата каретки (или перевода строки, в зависимости от вашей осве- домленности). Он может быть определен символом обратной косой черты (\). Обратная косая черта убирает специальное значение символов и при использовании в конце строки, как здесь, означает, что специальный сим- вол, вставленный в выражение, является возвратом каретки. Вдобавок к возврату каретки, мы вставляем пару фигурных скобок. Это обозначает объявление начала-конца функции, которую мы обрабатываем. Поскольку мы вставляем текст, мы не должны говорить программе sed, что его нужно пе- чатать. Строка 17 - безусловный переход на себя, указывающий программе sed переход на вершину всего обрабатываемого выражения. Когда это происхо- дит, мы завершаем поиск еще одного control-L и начинаем весь процесс снова. Таким образом, мы можем обработать все функции из одного файла независимо от того, сколько их там. Строка 18 является концом sed-выражения и содержит также имя фай- ла, которое должно быть передано программе sed. Это является частью обычного синтаксиса, принятого в sed, но выглядит несколько неуместным, так как не выделено специальным отступом. Когда все файлы обработаны, завершается внешний цикл и заканчивается работа командного файла. 4.2.3. strips - из командного файла Shell ------------------------------------------------------------ ИМЯ: strips ------------------------------------------------------------ strips Извлекает документирующий заголовок командного процессора. ФУНКЦИЯ Печатает начальные строки комментария к командному файлу командно- го процессора, что выражено буквой "s" в имени. Первая строка игнориру- ется для совместимости с командным процессором языка Си. ФОРМАТ strips файл [...] ПРИМЕР ВЫЗОВА strips *.sh Извлекает комментарии из всех командных файлов в текущем каталоге. ИСХОДНЫЙ КОД ДЛЯ strips 1 : 2 # @(#) strips v1.0 Strip shell comment header Author: Russ Sage 4 for FILE in $@ 5 do 6 cat $FILE | (read LINE; echo $LINE 7 while read LINE 8 do 9 if [ "`echo $LINE|cut -c1`" = "#" ] 10 then echo "$LINE" 11 else exit 12 fi 13 done) 14 done ПЕРЕМЕННЫЕ СРЕДЫ FILE Хранит каждое имя файла, полученное из командной строки. LINE Хранит каждую строку вводного текста, полученную из читаемого файла. ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН strips? Так же как нам нужны средства обработки документации для файлов с исходным кодом на Си, нам нужны и аналогичные средства для командных файлов командного процессора. Разница между извлечением комментариев из кода на языке Си и их извлечением из кода командных файлов командного процессора - в способе разграничения комментариев в исходном файле. Исходный файл на Си использует пару /* */, а командные файлы командного процессора применяют #, чтобы выделить остаток строки как комментарий. Обработка документации облегчается, когда командные файлы следуют некоторой форме стандартизованной документации. В действительности нет формального стандарта, но наиболее близкий стандарт приходит со страниц руководств по самой системе UNIX. Стандартными полями являются имя ко- мандного файла, способ его вызова, что он делает и, возможно, некоторые пометки о том, как он работает, ссылки на другие места для поиска ин- формации и сведения о том, какие файлы использует данный командный файл. Пример формата выглядит так: : # ИМЯ # strips Извлекает поля shell-комментариев # # ФОРМАТ ВЫЗОВА # strips файл [...] # # АВТОР # Russ Sage mm/dd/yy # # ОПИСАНИЕ # Данный командный файл извлекает комментирующие # заголовки из командных файлов интерпретатора shell. # # СМ. ТАКЖЕ # sh(1), cat(1) Отметим, что в первой строке имеется оператор :, который является для командного процессора нулевым оператором. Он ничего не делает, но всегда возвращает успешный статус выхода. Он находится в отдельной строке, поскольку это обозначает командный файл для Bourne shell. Если вы запускаете /bin/csh вместо /bin/sh, командные файлы могут вызвать путаницу. C Shell ищет символ # в первой колонке первой строки. Если он там есть, командный процессор считает этот файл командным файлом ин- терпретатора csh. Чтобы указать интерпретатору csh, что командный файл предназначен для интерпретатора Bourne shell, мы можем оставить эту строку пустой (что будет не слишком хорошо печататься и подлежит удале- нию) или поместить оператор, который сообщает интерпретатору C Shell, что это командный файл для Bourne shell, но ничего не делает под управ- лением Bourne shell. ЧТО ДЕЛАЕТ strips? Strips читает командный файл и печатает все комментарии с начала файла, которые являются непрерывными (т.е. печатает, пока не достигнет строки, не являющейся комментарием). Первая строка игнорируется, но пе- чатается. Когда строка не имеет символа # в первой символьной позиции, strips прекращает чтение этого файла. Командный файл должен иметь структуру комментария, аналогичную структуре, показанной в нашем предыдущем примере. Символы # должны быть в первой позиции, и каждая строка должна иметь символ #. Если у вас есть пустая строка где-нибудь в начальном блоке комментариев, strips печатает только первую часть блока. ПРИМЕРЫ 1. $ strips `kind /bin /usr/bin` Блоки комментариев извлекаются из текстовых файлов, размещенных в каталогах /bin и /usr/bin. 2. $ find / -name "*.sh" -print | while read FILE > do > strips $FILE > /tmp/doc/$FILE > done Find порождает список всех имен файлов, который попадает в strips. Выход strips направляется в каталог документации в /tmp. Окончательный выход попадает в файл с точно таким же именем, как исходный файл, толь- ко выход помещается в /tmp, поэтому никаких случайных удалений не про- исходит. ПОЯСНЕНИЯ Строки 4 и 14 окаймляют внешний цикл, который подает имена файлов данному командному файлу. Обработки или проверки ошибок нет. Пока в ко- мандной строке есть файлы, цикл продолжается. Вы можете, конечно, про- верить наличие аргументов, правильность и существование файлов. Для этого, мы думаем, вы видели достаточно примеров проверки ошибок, чтобы добавить их, куда вам нужно. Поэтому мы иногда опускаем такие фрагменты в нашем коде, чтобы сэкономить место и выделить главную функцию. Строка 6 применяет команду cat к файлу, который сейчас обрабатыва- ется. Выход направляется в конвейер, чтобы его прочитал другой shell. Новый shell получает длинную командную строку (обозначенную скобками в строках 6 и 13). Первая команда read читает самую первую строку. Поскольку мы не собираемся проверять эту строку, мы отображаем ее, а затем опускаемся в цикл последовательного чтения. Предполагается, что первая строка начинается с двоеточия, но если она начинается с символа #, она все равно печатается, так что вы не будете терять текст. Строки 7-13 являются внутренним циклом while, читающим строки текста со стандартного ввода, который был выходом команды cat. Когда текст заканчивается, прекращается и цикл while. Строки 9-12 - это строки принятия решения. Сначала мы отображаем текущую вводную строку и передаем ее по конвейеру команде cut. Выреза- ется первый символ, затем команда сравнения проверяет, совпадает ли он с символом комментария. Если да, строка отображается на стандартный вы- вод. Если это не символ комментария, то нужно достичь конца блока ком- ментариев, поэтому происходит выход из внутреннего цикла. Управление возвращается во внешний цикл (for), который стартует и берет следующее имя файла. Когда все файлы обработаны, strips завершается. 4.3. ctags - создание файла признаков исходного кода проекта ------------------------------------------------------------ ИМЯ: ctags ------------------------------------------------------------ ctags Делает файл признаков исходного кода для простоты доступа с помощью утилиты vi. ФОРМАТ ctags [файл ...] ПРИМЕР ВЫЗОВА ctags proj*.c Делает файл признаков для всего исходного кода проекта. ИСХОДНЫЙ КОД ДЛЯ ctags 1 : 2 # @(#) ctags v1.0 Create a C source code tag file Author: Russ Sage 4 awk -F'(' '/^[a-zA-Z_][a-zA-Z0-9_]*\(/ { 5 printf ("%s\t%s\t/^%s$/\n", $1, FILENAME, $0) }' $@ | sort -u +0 -1 ПЕРЕМЕННАЯ СРЕДЫ FILENAME awk Переменная, содержащая имя файла. ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН ctags? UNIX создан как среда для разработки программного обеспечения. Она поддерживает и поощряет модульность исходного кода программы. Модуль- ность - это концепция разбиения проекта на отдельные файлы, превращения идей в подпрограммы и компиляции отдельных файлов с исходным кодом в перемещаемые модули для последующей их сборки в исполняемый модуль. Такая философия разработки программного обеспечения может, однако, породить некоторые проблемы. Главная проблема - попытка получить неко- торого рода сцепку из всех маленьких кусков головоломки. Делаются вызо- вы подпрограмм, которые находятся в других файлах, возможно даже в дру- гих каталогах. Нужен инструмент, позволяющий нам, людям, посмотреть на программное обеспечение человеческим взглядом, т.е. содержательно, а не с точки зрения физического размещения. Этот подход чем-то аналогичен чтению книги в сравнении с чтением компьютерной распечатки. Распечатка заставляет вас делать последовательный просмотр, а книга допускает пря- мой доступ (и обычно предоставляет оглавление и предметный указатель для поиска специфических пунктов). Ctags преодолевает этот разрыв, создавая файл специального форма- та, который распознают редакторы vi и ex. Этот файл содержит "призна- ки", которые могут быть использованы при работе с редактором для обеспечения автоматического доступа к любой нужной функции, не требую- щего от вас знаний о том, в каком файле находится функция. Фактически, ctags предоставляет вам предметный указатель для груп- пы файлов с исходным кодом на языке Си. Когда вы объединяете его с ре- дактором, вы можете быстро найти любую функцию по известному вам имени и посмотреть тело функции. Это значит также, что вы можете легко копи- ровать и вставлять функции в любой исходный файл, с которым вы сейчас работаете. Если редактор не имел возможности работы с признаками или мы не построили инструментальное средство, использующее такое преимущество, то мы должны запускать grep для имени функции на наборе исходных файлов на Си (в надежде, что у нас есть подходящие файлы!), отмечать, какой файл имеет требуемую функцию, входить в этот файл редактором (вручную вводя все символы имени файла), а затем набирать символы шаблона по- иска. Это большая работа, которая может занять много времени. Благодаря использованию возможности работы с признаками, для файла признаков, из- влеченных из исходного кода, вся эта ручная работа сокращается. Это сочетание возможностей иллюстрирует то, чему не часто прида- ется значение: владельцы UNIX всегда настороженно относятся к возмож- ности использовать преимущества многочисленных средств, уже имеющихся в таких программах, как vi или ex. Зачастую от 90 до 95 процентов необхо- димых вам возможностей уже имеются, ожидая относительно простого ко- мандного файла интерпретатора shell, связывающего их вместе в мощный новый инструмент. Ctags уже существует в виде исполняемого модуля в системе Berkely (BSD) и в нынешней AT&T System V. Он происходит из системы Berkely, но поддерживается теперь в System V. Это иллюстрация взаимодействия между этими двумя источниками в мире UNIX, поскольку они взаимствуют полезные идеи друг у друга. Данное конкретное воплощение ctags является команд- ным файлом утилиты awk, имитирующим исполняемую программу из системы Berkely, а это значит, что пользователи систем XENIX и предыдущих версий AT&T могут теперь извлечь пользу от применения ctags. Еще одно преимущество версии в виде командного файла в том, что его можно легко модифицировать, чтобы обрабатывать другие особенности языка Си. Такое вы не можете делать с исполняемым модулем, если только у вас нет доро- гостоящей лицензии на исходный код. ЧТО ДЕЛАЕТ ctags? Ctags просматривает файлы с исходным кодом на Си, переданные в ко- мандной строке, и печатает список имен функций в каждом исходном файле. Имена функций имеют специальный синтаксис и должны быть именно в таком формате, иначе awk не распознает их как таковые. Эти правила заключа- ются в том, что имя функции должно находиться в начале строки, состоять из разрешенных символов и за ним должна следовать левая скобка. Пробелы в имени функции не допускаются. Вот пример модуля программы на Си, по- даваемого на рассмотрение командному файлу ctags: main() { } func1(arg1,arg2) int arg1,arg2; { } func2(arg1,arg2)int arg1,arg2; { } Результат работы ctags направляется в стандартный вывод (на эк- ран), поэтому он должен быть перенаправлен, чтобы попасть в файл. Вхо- дом для ctags является любое число имен файлов. Напомним, что если на входе имеется несколько файлов, то выход представляет собой один непре- рывный поток данных, попадающий в один файл. Если вам нужен выводной файл для каждого входного файла, то для управления ctags можно приме- нить такой командный файл с циклом: for F in *.c do ctags $F > $F.tags done Выход ctags состоит из трех полей в таком формате: признак имя_файла шаблон_поиска Реальный выход для примера программы на Си, приведенного выше, был бы таким: main /usr/russ/src/program.c /^main()$/ func1 /usr/russ/src/program.c /^func1(arg1,arg2)$/ func2 /usr/russ/src/program.c /^func2(arg1,arg2)$/ Первое поле является именем признака (которое совпадает с именем функции). Второе поле - маршрутное имя файла, содержащего данную функ- цию. Третье поле - шаблон поиска, используемый признаковыми средствами редактора для доступа к функции внутри файла (более подробно об этом - позже). Предположим, вы можете сгенерировать правильный файл признаков. Как согласовать файл признаков с редакторами таким образом, чтобы вы могли найти интересующую вас функцию? Редактор vi предоставляет много путей для этого. Первый способ - поместить имя используемого файла признаков в файл .exrc. (Файл .exrc является аналогом файла .profile для редактора ex и работает также с редактором vi, что не удивительно, так как vi построен na ex. Поскольку vi - наиболее популярный редактор системы UNIX, мы применяем его здесь для наших примеров.) Вы можете иметь файл .exrc, который выглядит примерно так: set tags=/usr/russ/mytags Впоследствии, когда вы обращаетесь к некоторому признаку, исполь- зуется данный файл признаков. Другой способ - установить файл признаков после того, как вы вошли в редактор. Чтобы посмотреть, каким является ваш файл признаков по умолчанию, введите, находясь в vi, следующее: :set tags Эта команда печатает файл признаков, о котором она знает. Для из- менения определенного в настоящий момент файла признаков, используйте синтаксис, который был в примере файла .exrc: :set tags=/usr/russ/mytags Теперь, когда редактор знает, в каком файле искать признаки, к ко- торым вы обращаетесь, давайте рассмотрим, как обращаться к признаку (т.е. к имени функции). Первый способ - объявить его в командной строке при вызове редактора. Например: $ vi -t tag Если вы уже находитесь в редакторе vi, можете применить такую ко- манду для поиска признака: :ta tag Двоеточие означает, что мы направляемся в ex, чтобы выполнить этот поиск. Мы просим ex найти указанную строку как признак, который разме- щается в текущем файле признаков. Когда этот признак найден в файле признаков, редактор vi редактирует файл с соответствующим именем, кото- рое он берет из поля 2. Это аналогично команде ":e имя_файла". Когда новый файл внесен в буфер редактора, последнее поле файла признаков используется в качестве строки шаблона поиска. Синтаксис точно такой же, как если бы вы набирали его вручную. Курсор перемещается в позицию в файле, которая соответствует строке поиска, при этом вы попадаете на интересующую вас функцию. ВЗАИМОСВЯЗЬ МЕЖДУ ex И vi Несколько отклоняясь от темы, рассмотрим два файла: /bin/ ex и /bin/vi. Небольшое исследование обнаруживает, что на самом деле это один и тот же файл. Мы можем проверить это, посмотрев на их индексные описатели файлов. Введите такую команду: $ ls -li `path ex vi` Выход показывает, что два числа в первой колонке одинаковы. 510 -rwx--x--t 5 bin bin 121412 Sep 19 1985 /bin/ex 510 -rwx--x--t 5 bin bin 121412 Sep 19 1985 /bin/vi Это число и есть индексный описатель файла (inode). Поскольку оба файла являются одним и тем же, вызов любого из них запускает один и тот же исполняемый модуль. Каким образом он знает, как вы его вызвали? Программа смотрит на строку argv[0] и видит, какое имя вы использовали для вызова файла. Затем редактор устанавливает свой интерфейс в соот- ветствии с тем, как вы его вызвали. Обратите внимание, что эта программа имеет пять связей. Как нам найти все другие имена, которыми можно вызвать vi и ex? Мы можем использовать команду системы UNIX ncheck. Эта команда воспринимает ин- дексный описатель файла и печатает все файлы, имеющие такой описатель файла . Примеры таких команд: $ ncheck -i 510 /dev/root $ ncheck -i 510 Первый синтаксис указывает команде ncheck искать файлы с inode, равным 510, только в корневой файловой системе. Ncheck ограничена по- иском в одной файловой системе. Это подкрепляет тот факт, что файлы не могут быть привязаны к различным файловым системам, поскольку каждая файловая система начинается с inode 2 и последовательно наращивается. Каждая файловая система имеет inode 510, который уникален для каждой файловой системы. Выход предыдущей команды выглядит так: dev/root: 510 /bin/edit 510 /bin/ex 510 /bin/vedit 510 /bin/vi 510 /bin/view Если файловая система не указана, как во втором примере, выполня- ется поиск по всем файловым системам, смонтированным в настоящее время. Это не слишком хорошо для нас, поскольку пять связей vi должны нахо- диться в одной и той же файловой системе. В противном случае файлы были бы связаны поперек границ файловых систем. Мы уже можем сказать, что никаких файлов редактора нет в каталоге /usr, размещенном во втором разделе диска от корневой файловой системы. ПРИМЕРЫ 1. $ ctags *.c Генерирует файл признаков для всех файлов с исходным кодом на Си в текущем каталоге. Выход направляется на экран в отсортированном порядке для каждого файла. Порядок файлов алфавитный, так как указано расшире- ние файла. Конечно, в большинстве случаев вы захотите перенаправить вы- ход ctags в файл. 2. $ ctags `Find /usr/src -name "*.c" -print` Этот синтаксис использует командную подстановку, чтобы найти все имена файлов, оканчивающиеся на .c и поместить их в командную строку. Проблема в том, что если найдено слишком много файлов, командная строка ctags может переполниться и испортить всю команду. В зависимости от серьезности переполнения, она может испортить и весь ваш процесс shell. 3. $ find /usr/src -name "*.c" -exec ctags {} \; > tags Находит все исходные файлы на Си в сегменте дерева /usr/src. Для каждого подходящего файла запускает программу ctags на этом файле. Использование такой формы записи предотвращает порчу вашей команды (о которой только что шла речь), а также запускает ctags для каждого имени файла. Весь выход помещается в файл tags для последующего использова- ния. 4. $ find /usr/src -type f -print | sort | > while read FILE > do > ctags $FILE > done >> tags Используя преимущество множественных каталогов, находит и сортиру- ет все файлы из каталога /usr/src и печатает их маршрутные имена в стандартный вывод. Затем они сортируются и поступают по конвейеру в цикл while. Цикл while используется для обслуживания сколь угодно боль- шого числа файлов без переполнения буферов. Выход цикла while добавля- ется к одному файлу - tags. Этот цикл громоздкий и медленный, но он вы- полняет свою работу. 5. $ find /usr/src -print | ctags Это неправильный способ использования ctags. Выходом команды find являются маршрутные имена. Ctags читает стандартный ввод, поскольку в командной строке нет файлов. Получается, что данные, которые читает awk, являются маршрутными именами от find, которые не имеют корректных полей для соответствия шаблонам функций. Никакого сопоставления не про- исходит. Аналогичную проблему могла бы вызвать такая командная строка: find /usr -print | wc -l Вы могли бы интерпретировать это так: "посчитать, сколько строк имеется во всех файлах каталога /usr". Но в действительности здесь ска- зано: "сколько имен файлов имеется в древовидной структуре /usr". Для подсчета общего количества строк в этих файлах нужен такой синтаксис: find /usr -exec cat {} \; | wc -l который гласит: "найти все файлы в /usr, распечатать каждый из них, затем посчитать, сколько строк в них имеется". Чтобы так же посту- пить с ctags, нужен такой синтаксис: find /usr/src -name "*.c" -exec cat {} \; | ctags В отличие от результата, который мы получили бы в предыдущих при- мерах: func1 /usr/russ/src/program.c /^func1(arg1,arg2)$/ func2 /usr/russ/src/program.c /^func2(arg1,arg2)$/ теперь выход будет выглядеть так: func1 - /^func1(arg1,arg2)$/ func2 - /^func2(arg1,arg2)$/ ПОЯСНЕНИЯ Символы "-" вместо имени файла появляются из-за того, что ctags читает из стандартного ввода. Awk автоматически присваивает своей внут- ренней переменной FILENAME значение "-", так как знает, что в командной строке не было файлов. Весь командный файл есть программа awk. Входом для командного фай- ла утилиты awk является $@, что представляет все позиционные параметры. Каждый параметр считается именем исходного файла на Си. Если никакие файлы не передаются в командной строке, awk ищет данные в стандартном входном потоке, но это создает некорректный выход, так как переменная FILENAME в awk имеет по умолчанию значение "-". Поскольку awk требует имена файлов, мы должны вызывать ctags с именами файлов, а не переда- вать ему данные по конвейеру через стандартный ввод, как показано в предыдущем примере. Awk читает каждый раз одну строку из файла данных и сверяет ее с шаблоном, пытаясь установить соответствие. Для каждой строки, соот- ветствующей шаблону, awk выполняет заданную программу. Первое, что де- лает ctags,- изменяет подразумеваемый разделитель полей утилиты awk с пробела на левую скобку. Благодаря использованию этого символа в ка- честве разделителя полей, строка определения функции разбивается на два поля: имя функции и остаток строки. Шаблон поиска утилиты awk соответствует синтаксису имени Си-функ- ции. Оно может начинаться с символов a-z, A-Z или символа подчеркива- ния. Далее в имени могут быть любые символы из набора a-z, A-Z, 0-9 и _. Между именем и скобкой нельзя использовать пробелы. Поиск начинается от начала строки (^), за которым следует последовательность допустимых символов (a-z, A-Z, 0-9), а затем левая скобка. Когда строка соответствует данному шаблону, генерируется выход с помощью оператора printf. Первое поле - строка, представленная обозна- чением $1. В данном случае $1 - это только имя функции, исключая левую скобку. Печатается символ табуляции, затем следующая строка, которая является переменной FILENAME из утилиты awk. Эта переменная должна быть получена из командной строки, иначе awk не будет знать имя файла, в ко- тором размещена данная функция, и файл признаков потеряет информацию, необходимую для доступа к файлу, содержащему функцию. Печатается еще одна табуляция, затем строка поиска. Строкой поиска является $0, что представляет всю строку, с которой работает awk. Строке предшествует символ ^, а за строкой следует символ $. Выход пропускается по конвейеру через sort с той целью, чтобы все признаки шли в отсортированном порядке. Опции сортировки указывают ути- лите sort проверять только первое поле и печатать только одно появление строки, если имеется несколько записей. МОДИФИКАЦИИ ДЛЯ ctags Теперь, когда мы знакомы с общим форматом редактируемого файла признаков, можем ли мы применить его для других полезных целей? Мы зна- ем, что мы можем идентифицировать регулярные структуры в программах на Сии создать признаки, с помощью которых можно получить доступ к этим структурам в редакторе. В программах на Си имеются не только имена функций, но и другие интересные конструкции, например имена структур. Их можно обслуживать с помощью модифицированной версии ctags. Единственное, что нам нужно знать,- это официальный синтаксис структуры данных. Структура данных, которая нас бы заинтересовала, часто имеет такой формат: struct name { int val1; char val2; }; Все, что мы должны сделать,- это заставить awk искать все появле- ния определения структуры. Затем мы можем построить файл признаков, в котором признаком является имя структуры. Этот файл, видимо, будет та- ким же, как и прежде, а строка поиска будет обнаруживать определение структуры, а не имя функции. Фактически, комбинация утилиты awk, приз- наков и редактора может быть использована для любого вида информации, которую вы можете захотеть хранить в файле специального формата, напри- мер для адресов, заметок, библиографических ссылок и т.д. Вам просто нужно подобрать соответствующие разделители и правильно их использо- вать. Мы надеемся, что облегчили сопровождение ваших программ и предло- жили вам идеи для других способов автоматической обработки документа- ции. Вы можете без особого труда учреждать и поддерживать локальные соглашения о документации с помощью командных файлов, аналогичных представленным здесь. Примером проекта, за который вы можете взяться, является согласование наших программ извлечения информации (stripf, stripc, strips) и других программ, которые вы пишете, таким образом, чтобы они могли читать файл-формирователь (makefile, см. Make(1)) и вы- давать полную документацию по всем исходным файлам, участвующим в дан- ном проекте.  * ГЛАВА 5. УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ I: *  УПРАВЛЕНИЕ ВРЕМЕНЕМ И ДЕЛОПРОИЗВОДСТВОМ УПРАВЛЕНИЕ ВРЕМЕНЕМ at выполнение задач в указанное время b порожденный shell фоновых задач greet своевременное приветствие с терминала lastlog сообщение времени последней регистрации timelog учет и статистика сеансов работы today печать календаря с отмеченной текущей датой УПРАВЛЕНИЕ ДЕЛОПРОИЗВОДСТВОМ jargon генератор технических терминов phone база данных с телефонными номерами office делопроизводитель УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ I: УПРАВЛЕНИЕ ВРЕМЕНЕМ И ДЕЛОПРОИЗВОДСТВОМ ВВЕДЕНИЕ Мы уже многое знаем о файлах и о том, как управлять файловой структурой. Пора рассмотреть, как мы можем использовать систему UNIX для управления множеством задач, которые составляют наш рабочий день и держат нас в курсе того, что делают другие пользователи. Термин "уп- равление личной информацией" (personal management) подразумевает, что вы хотите создать свою собственную ПЕРСОНАЛЬНУЮ рабочую среду и инструментальные средства. Мы предлагаем вам пакет программ, которые вы можете приспособить к вашим требованиям. Фактически мы в этой и следующей главе представляем четыре отдельных набора программ, каждый из которых посвящен определенному аспекту управления личной информаци- ей. Средства управления временем помогают нам спланировать выполнение задач компьютером, а также контролировать наше личное время. Управле- ние делопроизводством имеет дело с хранением и извлечением информации, а также с организацией доступа к различным функциям системы UNIX посредством простого в использовании интерфейса в виде меню. Для каждой из этих областей деятельности мы даем ее обзор, а за- тем представляем соответствующую группу средств. УПРАВЛЕНИЕ ВРЕМЕНЕМ Поскольку система UNIX имеет встроенные функции поддержки времени и часы, она может следить за временем. Объединение функций поддержки времени с возможностью автоматического запуска группы команд означает, что мы можем настроить их так, чтобы компьютер выполнял многие наши рутинные работы, связанные со временем. Мы также можем использовать компьютер для отслеживания нашего собственного времени. В данном разделе представлены инструментальные средства at, b, greet, lastlog, timelog и today. Командный файл at дает нам возможность сказать машине о том, что в указанное время необходимо сделать то-то и то-то (вывести на экран сообщение или выполнить какие-то другие команды). Задача запускается в фоновом режиме, так что мы можем продолжать другую работу, а фоновая задача выполнится автоматически в указанное время. Эта задача может состоять из любых разрешенных в UNIX команд, поэтому ее возможности очень гибкие. Мы просто предлагаем некоторые идеи, связанные с ее использованием. Вторым средством является командный файл b. Это обработчик фоно- вых задач. Очень часто при порождении фоновых процессов мы не можем узнать, когда они закончились. Для того, чтобы это определить, нам не- обходимо вручную просмотреть таблицу процессов или найти какой-то иной признак того, что данная работа завершена. Командный файл b запускает задачу, управляет операциями ввода-вывода и затем сообщает нам о том, что задача завершена. Командный файл greet показывает, каким образом переводить внут- реннее время компьютера в более понятные пользователю категории. Он различает три перида суток (утро, день и вечер) и реагирует на них соответствующими сообщениями. Это довольно просто, но обеспечивает неплохое основание для подхода к решению других проблем, связанных со временем. Далее мы представляем два средства, которые образуют базис систе- мы управления временем. При выполнении множества работ нам необходимо подсчитать время, которое мы потратили на данный проект, чтобы мы мог- ли выставить нашему клиенту соответствующий счет. Командный файл lastlog запускается автоматически, когда вы регистрируетесь в системе. Поддерживается база данных, в которую каждый раз записывается время вашей регистрации для последующего анализа или хранения записей. С этим инструментальным средством соседствует командный файл timelog. Это утилита, которая выполняет подсчет времени. Она может следить за общим времене