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

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

Содержание


Возможные модификации
Возможные исследования
Формат вызова
Текст программы
Переменные среды выполнения
Возможные исследования
Формат вызова
Текст программы
Подобный материал:
1   2   3   4   5   6   7   8   9   ...   45

ется ноль. Это истинное значение, поэтому выполняется участок цикла

после then (строки 14-17). Если файл не существует или не является

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

Это приводит нас в конец цикла, выполняется следующая итерация цикла

while и мы рассматриваем следующий аргумент из стандартного ввода.

Теперь рассмотрим обработку, выполняемую по then (строки 14-17).

Для каждого файла, который является текстовым, печатается строка двое-

точий (:) до и после имени файла, а команда head системы UNIX печатает

первые 15 строк. Такой сценарий продолжается, пока не закончатся дан-

ные в стандартном вводе.

Рассмотрим другую альтернативу, покрываемую данным оператором вы-

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

параметров (что указано символом * в операторе case). Цикл for пробе-

гает все параметры (строка 20). Звездочка (*) в операторе case означа-

ет, что подходит любое значение, которое не подошло ранее. Это улавли-

вающая (catchall) опция. Цикл for использует аргумент $* в качестве

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

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

Команда find используется для поиска всех нормальных файлов в ка-

талоге. "Нормальные" файлы не означает "только текстовые файлы", поэ-

тому мы проверим это позже. Выход команды find передается по каналу

команде sort, чтобы сделать его более наглядным. Отсортированный

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

переменную FILE (строка 27). Проверяется, текстовый ли файл, затем он

печатается командой head.

Если мы сравним строки 13-18 и строки 24-29, то мы увидим, что

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

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

ее, когда нужно. Язык программирования интерпретатора shell, хотя и

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

Последний интерпретатор shell в System V имеет функции, которые позво-

ляют решить эти проблемы.

Отметим, что внутренний цикл while повторяется на каждом файле,

который существует в определенном каталоге, а внешний цикл for прохо-

дит от каталога к каталогу.


ВОЗМОЖНЫЕ МОДИФИКАЦИИ


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

переходить на команду find непосредственно из thead. Полезными аргу-

ментами были бы -name для изолирования образцов имен файлов и -ctime

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

Еще одной привлекательной особенностью было бы добавление опции

грамматического разбора (основанной на -) и опции -n, указывающей, что

из команды head должно быть напечатано n строк.


ВОЗМОЖНЫЕ ИССЛЕДОВАНИЯ


В чем отличие между двумя следующими операторами?


$ find $HOME -name "*.c" -print | thead


и


$ find $HOME -name "*.c" -exec head {} \;


Они выглядят очень похоже, и они действительно похожи. Они обра-

батывают одни и те же файлы и печатают одни и те же данные из каждого

файла. Основное отличие в том, что строка, которая использует thead,

печатает хорошее оформление вокруг имени файла, а чистая команда find

печатает непрерывный поток текста так, что очень трудно определить,

какой файл вы просматриваете.


2.1.3. tgrep - поиск строк в дереве файловой системы


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


ИМЯ: tgrep

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


tgrep Поиск строки по шаблону в дереве файлов


НАЗНАЧЕНИЕ


Обходит файловое дерево и ищет в каждом файле указанную строку.

Если не указан никакой каталог, tgrep действует как фильтр.


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


tgrep [-c|-h] string [file ...]


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


# tgrep "profanity" /


Поиск слова "profanity" по всей системе (суперпользователь снова

на тропе войны!)


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


1 :

2 # @(#) tgrep v1.0 Search for string in tree Author: Russ Sage

2а Поиск строки в дереве


4 OPT=""


6 for ARG in $@

7 do

8 if [ "`echo $ARG|cut -c1`" = "-" ]

9 then case $ARG in

10 -c) OPT="-name \"*.c\""

11 shift;;

12 -h) OPT="-name \"*.h\""

13 shift;;

14 *) echo "$O: incorrect argument" >&2

15 echo "usage: $O [-c|-h] string [file ...] >&2

16 exit 1;;

17 esac

18 fi

19 done


21 case $# in

22 0) echo "$O: argument error" >&2

23 echo "usage: $O [-c|-h] string [dir ...]" >&2

24 exit 2

25 ;;

26 1) while read FILE

27 do

28 grep -y "$1" $FILE /dev/nul

29 done

30 ;;

31 *) STRING=$1; shift

32 eval find "$@" -type f $OPT -print | sort | while read FILE

33 do

34 grep -y "$STRING" $FILE /dev/null

35 done

36 ;;

37 esac


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


FILE Содержит имя каждого файла

OPT Содержит специальные опции команды find

STRING Временная переменная, в которой содержится строка

поиска


ОПИСАНИЕ


ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ tgrep?


Как мы могли видеть на примере двух предыдущих утилит, рекурсив-

ный просмотр файлов очень полезен. Он сохраняет время, поскольку поз-

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

рые могут быть использованы в более мощных утилитах. Чем больше име-

ется созданных нами средств, тем больше новых средств мы можем постро-

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

должны позаботиться об их взаимозависимости (каким утилитам или

средствам требуются другие утилиты или средства и кто на кого влияет).

Еще одна область, где UNIX не имеет "родной" рекурсивной команды

- это обработка строк. Семейство команд типа grep очень велико, но все

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

Нам необходим препроцессор для команды grep. Правда, мы можем дать

запрос на все файлы во всей системе или какой-либо ее части по нашему

выбору. Если мы попытаемся сделать это вручную, то это означает, что

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

таксической ошибке. Вы также должны точно помнить, как вы создавали

командную строку, если вы в следующий раз захотите выполнить такую же

задачу. Зачем выполнять грязную работу? Для этого существует компь-

ютер.

Создавая программу автоматического обхода дерева файлов, мы осво-

бождаемся для того, чтобы направить нашу энергию на более важные вещи

вместо того, чтобы выпутываться из ситуации в случае какого-либо слиш-

ком специфичного синтаксиса. Один раз создав достаточно мощные

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

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


ЧТО ДЕЛАЕТ tgrep?


Основным предназначением tgrep является обеспечение большей гиб-

кости и легкости использования возможностей команды grep. Ее синтаксис

точно такой же, как и у grep, за исключением допустимых типов файлов.

В команде grep UNIX в качестве аргумента может указываться практически

любой файл, но указание текстового файла имеет наибольший смысл. В ко-

манде tgrep также могут использоваться текстовые файлы, но наибольший

смысл имеет указание каталогов, поскольку мы ищем имена файлов. Коман-

да find работала бы не очень хорошо, если бы пыталась извлечь множест-

во имен файлов из текстового файла. В командной строке может указы-

ваться множество имен каталогов, поскольку все они используются как

начальное место поиска оператора find.

По умолчанию tgrep находит все обычные файлы. В этой программе

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

пытаемся напечатать все на экран. Поэтому мы ищем строки символов во

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

Если вы хотите выбрать типы файлов, используйте две опции, -c и

-h. Опция -c заставляет команду UNIX find искать только файлы с имена-

ми вида *.c. Аналогично, опция -h соответствует файлам *.h. Эти опции

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

мы более детально познакомимся в главе 4. Эти опции не самое важное,

но они показывают, как легко добавить новые опции в программу.

Если при вызове была указана какая-то иная опция, кроме упомяну-

тых, выводится сообщение об ошибке и программа останавливается. Подоб-

но thead, tgrep является фильтром.


ПРИМЕРЫ


1. $ tgrep unix $HOME


Поиск любого вхождения слова unix во всех файлах моего регистра-

ционного каталога.


2. $ tgrep -c "sleep()$" $HOME/src


Поиск выражения (начало строки, символьная строка, конец строки)

во всех исходных файлах на языке Си в регистрационном каталоге с

исходными текстами (опция -c).


3. # find /usr/src -name "*.c" -print | tgrep "ioctl"


Поиск всех вызовов ioctl в исходных Си-файлах, начиная с каталога

/usr/src. (Обратите внимание, что я являюсь суперпользователем. Это

видно из того, что я занимаюсь поиском в ограниченной части системы, а

именно в исходных дистрибутивах, а также из того, что в качестве сим-

вола приглашения используется символ "#".)


4. $ tgrep "| more" `find . -type f -print`


Поиск символа вертикальной черты (|), после которого следует сло-

во more, в списке имен файлов, генерируемом оператором find. Find пе-

чатает имена всех файлов текущего каталога и всех подкаталогов, кото-

рые являются обычными файлами.


5. $ tgrep trap /bin /usr/bin /etc


Поиск команды прерывания (trap) во всех командных файлах интерп-

ретатора shell, которые имеются в трех каталогах.


ПОЯСНЕНИЯ


В строке 4 переменная OPT, в которой хранятся необязательные ко-

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

Строки 6-18 выполняют проверку на наличие ошибок. Проверяется,

является ли первым символом каждого позиционного параметра символ "-".

Если проверка успешна, то проверяется на корректность сам аргумент.

Возможными опциями являются -c и -h. Если указано что-либо другое, вы-

водится сообщение об ошибке и программа завершается. Обратите внима-

ние, что с помощью команды shift допустимые опции удаляются из команд-

ной строки. Это сделано для того, чтобы впоследствии выражение $@ мог-

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

маршрута. Выражение $@ является другой формой выражения $ *, в которой

оно распространяется на все позиционные параметры. Однако в последую-

щем тексте мы увидим одно большое отличие между ними.

Еще один трюк сделан при присвоении значения переменной OPT в

строке 10. Этой переменной нам необходимо присвоить значение, которое

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

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

Однако обнаружение второй кавычки обычно ЗАВЕРШАЕТ оператор присваива-

ния, а мы этого в данном случае не хотим. Выходом из ситуации является

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

щего за ним символа. В данном случае специальное значение было бы кон-

цом строки присваивания.

Таким образом, кавычка перед звездочкой (*) в строке 10 рассмат-

ривается как часть значения переменной OPT, вместо того, чтобы завер-

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

быть сохранена в значении переменной, чтобы указать конец хранимой

здесь строки поиска, поэтому она также экранирована символом \.

Последняя кавычка в этой строке не экранирована обратной косой чертой.

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

ра shell и завершает оператор присваивания.

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

понять его. Когда какой-то командный файл не желает работать правиль-

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

Оставшаяся часть командного файла - это оператор case (строки

21-37), который имеет дело с числом аргументов командной строки. Если

нет никаких аргументов, печатается сообщение об ошибке и мы выходим из

программы. Мы ведь не можем делать никакого поиска командой grep, если

мы даже не знаем, какую строку нужно искать.

Если указан только один аргумент, то это должна быть строка по-

иска. Это также означает, что в командной строке не указаны имена фай-

лов и поэтому мы читаем их со стандартного устройства ввода, т.е. ко-

мандный файл действует как фильтр. Цикл while (строки 26-29) читает со

стандартного ввода имя каждого файла для поиска в нем командой grep

нужной строки. Опция -y команды grep означает нечувствительность к ре-

гистру символов при поиске. Использование этой опции дает нам хорошие

шансы попасть на нужную строку.

Другой интересной особенностью этого цикла являются две команды

grep в строках 28 и 34. Мы ищем нужную строку не только в файле, но

также в каталоге /dev/null. Это кажется странным? Да. Проблема заклю-

чается в самой команде grep. Grep знает, когда в командной строке ука-

зано более одного файла. Если имеется более одного файла, то имя файла

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

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

считается, что пользователь должен помнить это имя. Это создает проб-

лемы при написании командных файлов, когда вы имеете много имен фай-

лов, но они обрабатываются по одному. Мы обрабатываем файлы по одному,

поскольку если бы мы использовали указание группового имени файлов с

помощью метасимволов, то могло бы оказаться слишком много имен файлов

в одной командной строке для команды grep. Поэтому вместо использова-

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

мы избегаем этого путем обработки в цикле каждого файла по очереди.

Поскольку на самом деле мы ищем большое количество разных строк, то мы

хотим видеть имена файлов, в которых они были найдены.

При указании в командной строке grep каталога /dev/null, grep

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

поскольку теперь имеется два имени файла в качестве аргументов. Конеч-

но, в /dev/null ничего нельзя найти, поскольку он пуст по определению.

Последний образец в операторе case (строки 31-36) - это ловушка.

Он соответствует любому количеству позиционных параметров сверх одно-

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

строке.

Даже хотя мы можем указать несколько каталогов в командной стро-

ке, первым аргументом по-прежнему является строка поиска. Не так легко

сказать: "начиная со второго параметра", поэтому мы сохраняем строку

поиска (в переменной STRING - Прим. перев.) и убираем ее командой

shift. Теперь мы можем получить доступ к остальной части командной

строки с помощью выражения $@.

Давайте посмотрим, что происходит, когда мы ссылаемся на перемен-

ную $OPT для получения опций команды find. Допустим, мы вызвали tgrep

с опцией -c. Когда мы присваиваем значение переменной OPT, мы ставим

строку c в виде "*.c" внутри двойных кавычек, поскольку мы не желаем,

чтобы shell раскрывал эту строку (т.е. трактовал ее как имена всех

файлов, соответствующих данному образцу) именно сейчас. Теперь, как

только к переменной $OPT есть обращение в команде find, значением OPT

является *.c, что означает поиск файлов, символьные имена которых

соответствуют *.c. Для отмены символьной интерпретации мы должны

использовать команду eval. Перед выполнением команды find команда eval

заставляет shell повторно анализировать команду в отношении распрост-

ранения значения переменной. На этом проходе выражение "*.c" превраща-

ется в *.c, что разрешает генерацию имен файлов таким образом, чтобы

были просмотрены все эти файлы.

Указывая $@ в команде find, мы можем производить поиск во всех

каталогах сразу. Таким образом, нам нужно вызвать find только один

раз, что позволяет сберечь время центрального процессора. Одной из ин-

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

было то, что выражение вида $* не работало. В команде find возникала

ошибка сохранения. Даже запуск shell'а в режиме -x (установленный флаг

разрешения выполнения) не разрешил проблему. Изменение синтаксиса, ка-

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

ражение $* раскрывается в "$1 $2 ...", в то время как выражение $@

превращается в "$1" "$2" (т.е. в отдельные аргументы). Происходило то,

что выражение $* передавало имена нескольких каталогов команде find

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

поэтому прекращала выполнение. Когда же вместо этого было использовано

выражение $@, команда find получила несколько независимых строк с име-

нами. Это вполне подошло команде find, и все заработало. Такие мелкие

детали всегда требуют много времени для изучения!


ВОЗМОЖНЫЕ ИССЛЕДОВАНИЯ


В чем разница между двумя следующими операторами?


grep "$1" `find "$2" -print`


и


find "$2" -print | while read F

do

grep "$1" $F

done


Они кажутся совершенно похожими, но существует различие в глав-

ном. Первый оператор - это один вызов команды grep. Аргументами явля-

ются множество имен файлов, поставляемых командой find. Если find сге-

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

О том, что сгенерировано слишком много имен файлов, никакого предуп-

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

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

щем случае.

Второй оператор - это цикл. Он работает медленнее и вызывает grep

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

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

йеру, который фактически не имеет ограничений на число данных, которое

он может иметь. Наша программа никогда неожиданно не прекратит выпол-

нение.


2.1.4. paths - нахождение пути доступа к исполняемым файлам, со

специальными опциями


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


ИМЯ: paths

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


paths Определитель маршрутных имен файлов со специальными

опциями


НАЗНАЧЕНИЕ


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

имя файла в длинном формате или ищет файлы с установленным битом поль-

зовательского идентификатора (setuid bit files) в каталогах по указан-

ному маршруту.


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


paths [-l] [-s] file [file ...]


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


$ paths -l ed ex vi


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

мыми модулями редакторов ed, ex и vi


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


1 :

2 # @(#) paths v1.0 Path locator with special options Author: Russ Sage

2а Определитель местонахождения файлов со специальными опциями


4 FORMAT="path"


6 for ARG in $@

7 do

8 if [ '`echo $ARG | cut -c1`" = "-" ]

9 then case $ARG in

10 -l) FORMAT="ls"