Рассел Сейдж. Приемы профессиональной работы в unix перевод "Tricks of the unix masters" by Russel G

Вид материалаДокументы

Содержание


Пример вызова
Переменная среды
Пример вызова
Переменные среды
Пример вызова
Подобный материал:
1   ...   15   16   17   18   19   20   21   22   ...   45
(*/) используются соответственно шаблоны

/\/\*/ и / \*\//

Для интерпретации этих обозначений нужно вспомнить регулярные вы-

ражения ed, sed и grep. Регулярные выражения (РВ) должны быть ограниче-

ны (символом /). awk воспринимает два выражения, разделенные запятыми,

как начальный и конечный шаблоны для квалификации вводных строк.

В данном примере начальное выражение означает "от начала строки

(), вслед за действительным символом косой черты (который должен быть

экранирован, чтобы убрать его специальное значение, т.е. \/) и действи-

тельной звездочкой (\*), использовать все строки, обнаруженные вплоть

до конечного выражения". Этим выбирается только первое вхождение со-

поставляемого шаблона (т.е. первого блока комментариев), так как прог-

рамма awk заканчивает работу при обнаружении завершающей строки.

(Остальные блоки комментариев выбираются с помощью stripf вместе с име-

нем функции и аргументами.)

Для каждой строки, которая соответствует набору выражений от на-

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

Строки 17, 18 и 19 содержат предложение if-then= else, которое выполня-

ет всю работу. Если $0 (что является всей строкой) НЕ равно пробелу и

концу комментария (*/), печатается вся строка. Формируя таким образом

предложение if, мы печатаем первую строку и все последующие строки, по-

ка не доберемся до конца комментария.

Когда обнаружена последняя строка, результатом проверки является

значение "ложь", поэтому выполняется else-часть. Поскольку мы знаем,

что это последняя строка, мы печатаем ее для завершенности блока ком-

ментария, а затем выходим из awk. Отметим, что благодаря вложению этих

двух команд вместе в фигурные скобки ({}), они рассматриваются как одно

составное предложение.

После завершения awk производится эхо-отображение прогона формата

на экран и берется следующий файл. Так продолжается до тех пор, пока

все файлы в командной строке не будут обработаны.


4.2.2. stripf - из Си-функции


------------------------------------------------------------------------


ИМЯ: stripf

------------------------------------------------------------------------


stripf Извлекает документирующий заголовок

Си-функции.


ФУНКЦИЯ


Извлекает и печатает комментирующий заголовок, имя функции с пара-

метрами вызова и объявление типов параметров для всех функций в исход-

ном файле на Си.


ФОРМАТ

stripf file [...]

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

stripf lib1.c

Извлекает документирующие заголовки для всех функций в

файле lib1.c.


ИСХОДНЫЙ КОД ДЛЯ stripf


1 :

2 # @(#) stripf v1.0 Strip function header Author: Russ Sage


4 for FILE in $@

5 do

6 sed -n -e '

7 /L$/ {

8 s/L$/.bp/p

9 : loop

10 n

11 /{/b exit

12 p

13 b loop

14 : exit

15 i\

16 {}

17 b

18 }' $FILE

19 done


ПЕРЕМЕННАЯ СРЕДЫ


FILE Хранит имя файла для каждого файла

из командной строки.


ОПИСАНИЕ

ЗАЧЕМ НАМ НУЖЕН stripf?


Предположим, что наш код на языке Си соответствует модели докумен-

тации, представленной ранее при описании stripc. Тогда нам нужен способ

поддержания изменений в документации по ходу изменений кода. Мы видели,

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

будет изменена, когда изменится код. Проблема возникает, когда нам нуж-

на твердая копия документации, которая находится внутри исходного кода.

Как нам получить ее из файлов?


ЧТО ДЕЛАЕТ stripf?


Командный файл stripf решает эту проблему. Он просматривает весь

файл и печатает всю документацию для каждой ФУНКЦИИ, которая размещена

в этом файле (включая "main", если она есть).

Входом для stripf являются имена файлов, переданные в командной

строке. Stripf обрабатывает файлы по очереди и помещает выход в stdout.

Этот выход можно перенаправить, но вход должен быть в командной строке.

К выходу применяются дополнительные модификации, чтобы сформиро-

вать данные для среды утилиты nroff, поэтому выводные файлы можно фор-

матировать с помощью этой утилиты. Все прогоны формата заменяются на

команду .bp, принятую в nroff для начала страницы. Эта команда nroff

прогоняет страницу и увеличивает на единицу счетчик страниц. Возможно,

вы не хотите менять нажатие клавиш control-L на это значение, но вы

всегда можете указать в командном файле такие действия, какие вам нуж-

ны.

ПРИМЕРЫ


1. $ stripf module1.c | grep >"\.bp$" | wc -l

Печатает число модулей-функций, содержащихся в файле module1.c,

путем поиска каждого появления команд новой страницы программы nroff и

их подсчета. Мы знаем, что одна из таких команд является выходом для

каждой обнаруженной функции. Данную строку можно вложить в предложение

echo, которое говорит "имеется X модулей в файле $FILE".

2. $ for FILE in *.c ../*.c $HOME/src/*.c

> do

> stripf $FILE

> done >> /tmp/func.hdrs


Данный цикл for распространяется на все файлы в текущем каталоге,

которые оканчиваются на .c, все файлы в родительском каталоге с таким

же суффиксом и все файлы в подкаталоге src моего home-каталога с тем же

суффиксом. Из каждого файла извлекается документация о функциях и нап-

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

поэтому выход каждого вызова stripf ДОБАВЛЯЕТСЯ к файлу в /tmp.


ПОЯСНЕНИЯ


Вся программа - это один большой цикл for в строках 4-19. Этот

цикл присваивает переменной FILE каждое имя, имеющееся в командной

строке. Данный командный файл не имеет опций и обработки ошибок.

Команда sed системы UNIX вызывается для каждого имени файла. Прог-

рамма sed читает весь вход и выводит измененный текст в стандартный вы-

вод.

Опция -n используется в sed для подавления всего вывода, в проти-

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

этот флаг по той причине, что мы хотим указать программе sed, когда пе-

чатать выход. Опция -e применяется, чтобы сообщить программе sed, что

следующая последовательность текста между одинарными кавычками является

выражением, которое нужно вычислить.

Напомним, что sed - потоковый редактор, который читает одну стро-

ку, сверяет ее с выражениями, затем читает следующую строку и делает

все сначала. Первое, что мы ищем - символ control-L, стоящий в строке

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

так далее, пока не будет обнаружен control-L. (Еще раз напомним, что

вместо обозначения L в коде должен быть введен настоящий control-L.)

Когда обнаружен control-L, он активизирует все выражение, заклю-

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

начала страницы программы nroff, как описано ранее. Эта подстановка пе-

чатается, что является самым первым выводом. В строке 9 объявлена метка

"loop". Это не приводит ни к каким действиям, но устанавливает точку

перехода, которая впоследствии используется. (Управляющие структуры

программы sed довольно примитивны, но они позволяют описать выполняемую

работу.)

Строка 8 использует команду n программы sed, чтобы вызвать чтение

следующей строки. Мы разобрались с первой строкой - строкой, которая

содержит control-L - так что мы можем ее отбросить. В случае выполнения

цикла мы видим, что sed продвигается по нашему требованию, но не прод-

вигается сам.

Вспомним модель документации, рассмотренную ранее. Эта модель

включает документирующий заголовок для файла в целом, выполненный в

обычном стиле языка Си. Модель завершается символом control-L. Этот

первый блок обрабатывается с помощью stripc, как описано ранее. Мы не

хотим использовать его здесь при работе со stripf. Поэтому мы сейчас

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

Вслед за символом control-L имеется еще один набор из одной или

более строк комментария языка Си, которые описывают функцию, следующую

за ними. Далее идет само имя функции, объявление параметров и открываю-

щий символ самой функции, которым является левая фигурная скобка (}).

Строка 11 ищет эту фигурную скобку. Если она найдена, выполнение

переходит на метку exit (строка 14). Мы можем полагать, что мы все сде-

лали, если найдена левая фигурная скобка, так как этот символ должен

появляться только в начале функциимодуля. Когда мы находим фигурную

скобку, мы уже напечатали к этому моменту всю комментирующую информацию

и заголовок функции посредством строки 12, которую мы сейчас опишем. А

что если фигурная скобка появляется в поле комментария? Нет проблем,

поскольку поиск фигурной скобки привязан к началу строки с помощью сим-

вола . Он производит выражение, означающее "от первого символа в стро-

ке". Мы только тогда сопоставляем фигурную скобку этому выражению, ког-

да она встречается в качестве самого первого символа в строке.

Затем строка 12 предполагает, что мы еще не обнаружили фигурную

скобку и поэтому мы должны напечатать строку. Оператор p печатает теку-

щую строку, которую обрабатывает sed. Этот вывод направляется на экран.

Строка 13 - безусловный переход на метку loop. Отметим, что этот

переход только изменил процесс выполнения и не привел к чтению еще од-

ной вводной записи. С этим мы должны быть осторожны при управлении про-

цессом выполнения в программе sed. Мы только что напечатали текущую за-

пись, поэтому теперь мы должны отбросить ее и получить следующую за-

пись, что означает возврат в строку 9. Этот цикл печати и чтения следу-

ющей записи продолжается до обнаружения фигурной скобки, которая пере-

водит выполнение на метку exit.

Строка 14 - это метка exit. Когда мы попадаем на нее, мы знаем,

что был обнаружен control-L, напечатан комментирующий заголовок, напе-

чатаны имя функции и объявления ее параметров и найдена фигурная скоб-

ка. Заметим, что фигурная скобка еще не напечатана. Когда мы находим

ее, мы только делаем ветвление.

Строка 15 завершает вывод, вставляя некоторый текст в выводной по-

ток. Мы не можем сказать "печатать это в буквенном виде", поэтому про-

исходит движение вправо по тексту, как по команде echo. Мы должны сыг-

рать на правилах, установленных программой sed. Любой вывод должен быть

порожден обычными командами в стиле редактора ed. Вставка текста с по-

мощью команды "i" делает нам это. Отметим, что мы также вставляем сим-

вол возврата каретки (или перевода строки, в зависимости от вашей осве-

домленности). Он может быть определен символом обратной косой черты

(\). Обратная косая черта убирает специальное значение символов и при

использовании в конце строки, как здесь, означает, что специальный сим-

вол, вставленный в выражение, является возвратом каретки. Вдобавок к

возврату каретки, мы вставляем пару фигурных скобок. Это обозначает

объявление начала-конца функции, которую мы обрабатываем. Поскольку мы

вставляем текст, мы не должны говорить программе sed, что его нужно пе-

чатать.

Строка 17 - безусловный переход на себя, указывающий программе sed

переход на вершину всего обрабатываемого выражения. Когда это происхо-

дит, мы завершаем поиск еще одного control-L и начинаем весь процесс

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

независимо от того, сколько их там.

Строка 18 является концом sed-выражения и содержит также имя фай-

ла, которое должно быть передано программе sed. Это является частью

обычного синтаксиса, принятого в sed, но выглядит несколько неуместным,

так как не выделено специальным отступом. Когда все файлы обработаны,

завершается внешний цикл и заканчивается работа командного файла.


4.2.3. strips - из командного файла Shell


------------------------------------------------------------------------


ИМЯ: strips

------------------------------------------------------------------------


strips Извлекает документирующий заголовок

командного процессора.


ФУНКЦИЯ


Печатает начальные строки комментария к командному файлу командно-

го процессора, что выражено буквой "s" в имени. Первая строка игнориру-

ется для совместимости с командным процессором языка Си.


ФОРМАТ


strips файл [...]

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


strips *.sh

Извлекает комментарии из всех командных файлов в текущем каталоге.


ИСХОДНЫЙ КОД ДЛЯ strips


1 :

2 # @(#) strips v1.0 Strip shell comment header

Author: Russ Sage


4 for FILE in $@

5 do

6 cat $FILE | (read LINE; echo $LINE

7 while read LINE

8 do

9 if [ "`echo $LINE|cut -c1`" = "#" ]

10 then echo "$LINE"

11 else exit

12 fi

13 done)

14 done


ПЕРЕМЕННЫЕ СРЕДЫ


FILE Хранит каждое имя файла, полученное из

командной строки.

LINE Хранит каждую строку вводного текста,

полученную из читаемого файла.


ОПИСАНИЕ

ЗАЧЕМ НАМ НУЖЕН strips?


Так же как нам нужны средства обработки документации для файлов с

исходным кодом на Си, нам нужны и аналогичные средства для командных

файлов командного процессора. Разница между извлечением комментариев из

кода на языке Си и их извлечением из кода командных файлов командного

процессора - в способе разграничения комментариев в исходном файле.

Исходный файл на Си использует пару /* */, а командные файлы командного

процессора применяют #, чтобы выделить остаток строки как комментарий.

Обработка документации облегчается, когда командные файлы следуют

некоторой форме стандартизованной документации. В действительности нет

формального стандарта, но наиболее близкий стандарт приходит со страниц

руководств по самой системе UNIX. Стандартными полями являются имя ко-

мандного файла, способ его вызова, что он делает и, возможно, некоторые

пометки о том, как он работает, ссылки на другие места для поиска ин-

формации и сведения о том, какие файлы использует данный командный

файл. Пример формата выглядит так:

:

# ИМЯ

# strips Извлекает поля shell-комментариев

#

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

# strips файл [...]

#

# АВТОР

# Russ Sage mm/dd/yy

#

# ОПИСАНИЕ

# Данный командный файл извлекает комментирующие

# заголовки из командных файлов интерпретатора shell.

#

# СМ. ТАКЖЕ

# sh(1), cat(1)


Отметим, что в первой строке имеется оператор :, который является

для командного процессора нулевым оператором. Он ничего не делает, но

всегда возвращает успешный статус выхода. Он находится в отдельной

строке, поскольку это обозначает командный файл для Bourne shell. Если

вы запускаете /bin/csh вместо /bin/sh, командные файлы могут вызвать

путаницу. C Shell ищет символ # в первой колонке первой строки. Если он

там есть, командный процессор считает этот файл командным файлом ин-

терпретатора csh. Чтобы указать интерпретатору csh, что командный файл

предназначен для интерпретатора Bourne shell, мы можем оставить эту

строку пустой (что будет не слишком хорошо печататься и подлежит удале-

нию) или поместить оператор, который сообщает интерпретатору C Shell,

что это командный файл для Bourne shell, но ничего не делает под управ-

лением Bourne shell.


ЧТО ДЕЛАЕТ strips?


Strips читает командный файл и печатает все комментарии с начала

файла, которые являются непрерывными (т.е. печатает, пока не достигнет

строки, не являющейся комментарием). Первая строка игнорируется, но пе-

чатается. Когда строка не имеет символа # в первой символьной позиции,

strips прекращает чтение этого файла.

Командный файл должен иметь структуру комментария, аналогичную

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

в первой позиции, и каждая строка должна иметь символ #. Если у вас

есть пустая строка где-нибудь в начальном блоке комментариев, strips

печатает только первую часть блока.

ПРИМЕРЫ


1. $ strips `kind /bin /usr/bin`

Блоки комментариев извлекаются из текстовых файлов, размещенных в

каталогах /bin и /usr/bin.

2. $ find / -name "*.sh" -print | while read FILE

> do

> strips $FILE > /tmp/doc/$FILE

> done

Find порождает список всех имен файлов, который попадает в strips.

Выход strips направляется в каталог документации в /tmp. Окончательный

выход попадает в файл с точно таким же именем, как исходный файл, толь-

ко выход помещается в /tmp, поэтому никаких случайных удалений не про-

исходит.


ПОЯСНЕНИЯ


Строки 4 и 14 окаймляют внешний цикл, который подает имена файлов

данному командному файлу. Обработки или проверки ошибок нет. Пока в ко-

мандной строке есть файлы, цикл продолжается. Вы можете, конечно, про-

верить наличие аргументов, правильность и существование файлов. Для

этого, мы думаем, вы видели достаточно примеров проверки ошибок, чтобы

добавить их, куда вам нужно. Поэтому мы иногда опускаем такие фрагменты

в нашем коде, чтобы сэкономить место и выделить главную функцию.

Строка 6 применяет команду cat к файлу, который сейчас обрабатыва-

ется. Выход направляется в конвейер, чтобы его прочитал другой shell.

Новый shell получает длинную командную строку (обозначенную скобками в

строках 6 и 13). Первая команда read читает самую первую строку.

Поскольку мы не собираемся проверять эту строку, мы отображаем ее, а

затем опускаемся в цикл последовательного чтения. Предполагается, что

первая строка начинается с двоеточия, но если она начинается с символа

#, она все равно печатается, так что вы не будете терять текст.

Строки 7-13 являются внутренним циклом while, читающим строки

текста со стандартного ввода, который был выходом команды cat. Когда

текст заканчивается, прекращается и цикл while.

Строки 9-12 - это строки принятия решения. Сначала мы отображаем

текущую вводную строку и передаем ее по конвейеру команде cut. Выреза-

ется первый символ, затем команда сравнения проверяет, совпадает ли он

с символом комментария. Если да, строка отображается на стандартный вы-

вод. Если это не символ комментария, то нужно достичь конца блока ком-

ментариев, поэтому происходит выход из внутреннего цикла. Управление

возвращается во внешний цикл (for), который стартует и берет следующее

имя файла. Когда все файлы обработаны, strips завершается.


4.3. ctags - создание файла признаков исходного кода проекта


------------------------------------------------------------------------


ИМЯ: ctags

------------------------------------------------------------------------


ctags Делает файл признаков исходного кода для

простоты доступа с помощью утилиты vi.


ФОРМАТ


ctags [файл ...]


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


ctags proj*.c

Делает файл признаков для всего исходного кода проекта.


ИСХОДНЫЙ КОД ДЛЯ ctags


1 :

2 # @(#) ctags v1.0 Create a C source code tag file

Author: Russ Sage


4 awk -F'(' '/[a-zA-Z_][a-zA-Z0-9_]*\(/ {