Жанр: Учеба

Приемы профессиональной работы в Unix

сторінка №5

и.
Если вы хотите выбрать типы файлов, используйте две опции, -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"
11 shift;;
12 -s) FORMAT="set"
13 set "1";;
14 *) echo $0: arg error" "&2
15 echo "usage: $0 [-l] [-s] file [file ...]" "&2
16 exit 1;;
17 esac
18 fi
19 done

21 IFS="${IFS}:"

23 for FILE in $@
24 do
25 for DIR in $PATH
26 do
27 case $FORMAT in
28 path) if [ -f $DIR/$FILE ]
29 then echo $DIR/$FILE
30 fi;;
31 ls) if [ -f $DIR/$FILE ]
32 then ls -l $DIR/$FILE
33 fi;;
34 set) echo "\n:::::::::::::::::::"
35 echo "$DIR"
36 echo "::::::::::::::::::::"
37 ls -al $DIR | grep "^[^ ]*s[^ ]*";;
38 esac
39 done
40 done

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

ARG Содержит каждый аргумент командной строки
DIR Элемент с именем каталога в переменной PATH
FILE Содержит имя каждого файла в командной строке
FORMAT Тип требуемого формата выходных данных
IFS Переменная shell'а, разделитель полей
PATH Переменная shell'а, пути к каталогам исполняемых
модулей

ОПИСАНИЕ

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

В нашей среде интерпретатора shell переменная с именем PATH содержит
имена каталогов, отделенные друг от друга символами двоеточия
(:). Каждый раз, когда вы вводите команду после приглашения shell'а,
интерпретатор shell, начиная с первого каталога, указанного в переменной
PATH, смотрит, находится ли введенная вами команда в этом каталоге.
Если да, то команда выполняется. Если нет, то shell идет в следующий
каталог, указываемый переменной PATH, и так далее, пока не будут
проверены все каталоги. Если команда все же не будет найдена, то вы
получите следующее сообщение об ошибке:

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

|
| $ whatchamacallit
| sh: whatchamacallit: not found
|
|

Такой поиск команды осуществляется автоматически, но сама система
не сообщает вам, ГДЕ размещена команда. Нам необходима утилита, которая
ищет и выводит на экран маршрут к указанному файлу. Имея такую
утилиту, вы можете использовать кратчайшие пути получения того, что
вам нужно, и выполнять множество различных трюков. Вы можете комбинировать
подобную команду с другими командами для создания гораздо более
мощных средств. С маршрутом можно также комбинировать команды more,
ls, file и cd системы UNIX. Возможно, вы обнаружите и другие команды
по мере экспериментирования.
Команда, несколько похожая на ту, которую мы ищем, существует
где-то в мире системы UNIX Systev V. Например, в системе AT&T это команда
where. В системе UNIX Berkeley это команда which (текст на языке
Си-shell'а) или whereis (исполняемая программа). Whereis дает дополнительную
информацию, такую как место размещения файлов с исходными
текстами (в каталоге /usr/src). Увидев, как мы создаем нашу собственную
команду поиска маршрута, вы можете модифицировать ее для обеспечения
работы с особенностями некоторых других команд и приспособить такие
вещи к вашим нуждам.
Прежде чем мы удовлетворим свои прихоти, давайте бегло глянем на
команду path, более простую, чем paths. Вся программа выглядит примерно
так:

IFS="${IFS}:"
for FILE in $@
do
for DIR in $PATH
do
if [ -f $DIR/$FILE ]
then echo $DIR/$FILE
fi
done
done

Основная идея очень проста. Сперва мы добавляем двоеточие (:) к
разделителю полей. Нам необходимо сохранить значения, принятые по
умолчанию (пробелы, табуляции, символы новой строки), так, чтобы мы
могли все-таки обрабатывать командную строку с пробелами в качестве
символов-разделителей. Символ : дает нам возможность отдельно рассматривать
каждый маршрут, хранимый в переменной PATH.
Вся программа представляет собой два цикла for. Внешний цикл
просматривает имена всех файлов, указанных в командной строке. Внутренний
цикл последовательно обходит все каталоги, содержащиеся в переменной
PATH. Для каждого файла просматриваются все каталоги с целью
определения, содержит ли этот каталог файл с таким именем. Полное
маршрутное имя представляет собой комбинацию префикса-каталога и имени
файла (называемых именем каталога и базовым именем соответственно).
Встроенная shell-команда test использована для определения того, существует
ли файл в определенном каталоге.

Если ваша переменная PATH выглядит так:

PATH=.:/bin:/usr/bin:/etc/:$HOME/bin

то внутренний цикл выполнит пять итераций в таком порядке: ., /bin,
/usr/bin, /etc и, наконец, $HOME/bin. Если бы запрос имел вид "path
ll" для поиска утилиты, которую мы создадим позже в этой главе, то результат
мог бы выглядеть так:

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

|
| /usr/bin/ll
| /usr/russ/bin/ll
|
|

Это значит, что команда ll была найдена в двух местах из вашего набора
маршрутов поиска.

ЧТО ДЕЛАЕТ paths?

Теперь, когда мы знаем, как работает более простая команда path,
мы можем по достоинству оценить дополнительные возможности специальной
команды получения маршрута - команды paths. Paths имеет три основные
функции. Она может выполняться как основная команда path, которую мы
уже рассмотрели, и давать полное маршрутное имя исполняемого модуля.
Она может выдавать маршрут файла в длинном формате. Она также может
выдать список всех файлов с установленным пользовательским идентификатором
(setuid bit files), которые есть в ваших маршрутных каталогах.
(См. главу 8, где описаны биты setuid.)
Синтаксис вызова немного отличается для разных опций. Для того
чтобы использовать формат выдачи маршрута или формат команды ls, нужна
такая командная строка:

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

как, например, в командах "paths ls who date" или "paths -l ll". Для
поиска файлов с установленным пользовательским идентификатором (setuid
files) не нужно указывать имена файлов в командной строке. Вся команда
должна быть такой:

paths -s

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

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

ПРИМЕРЫ

1. $ paths ls more who paths
/bin/ls
/usr/bin/more
/bin/who
/usr/russ/bin/paths

Поиск маршрутов к командам ls, more, who, paths. При выводе указываются
полные абсолютные маршрутные имена. Обратите внимание, что в
конце имени каждого файла печатается символ новой строки, чтобы получить
распечатку, в которой каждое имя файла стоит в отдельной строке.

2. $ more `paths gettydefs termcap paths`

Если ваша переменная PATH содержит каталог /etc, то этот пример
будет работать. Если нет, то первые два файла не будут найдены. Сначала
запускается команда paths, и ее вывод помещается на свое место в
командной строке команды more. Когда запускается команда more, она не
знает, что ее аргументы получены от другой команды. После завершения
работы команды paths команда more принимает вид:

more /etc/gettydefs /etc/termcap /usr/russ/bin/paths

с полными маршрутными именами каждого файла. Этот пример показывает,
как можно заставить команду paths выполнять всю работу по поиску и показу
файлов, которые вы хотите увидеть.

3. $ ll `paths ll`

В этом примере в длинном формате выводятся файлы с именами ll,
которые найдет path. (Мы представим нашу версию команды ll несколько
позже в этой же главе.) Как и в предыдущем случае, сначала генерируется
информация о маршруте, затем она помещается в командную строку, а
затем запускается команда ll.

4. $ m `paths paths`

В данном примере генерируется маршрутное имя самого командного
файла paths и передается программе m, которая использует команду more
для распечатки. (Командный файл m мы также покажем вам позже.)

ПОЯСНЕНИЯ

В строке 4 инициализируется переменная FORMAT, указывая маршрутный
тип поиска. Выполняется действие по умолчанию, точно такое же, как
в командном файле path, который мы рассмотрели ранее.
В строках 6-19 все аргументы командной строки проверяются на корректность.
Критерием того, что аргумент есть опция, является дефис в
роли первого символа. Заметим, что здесь не разрешено использование
синтаксиса "-xyz". Это заставляет вас пользоваться синтаксисом "-x -y
-z". Хотя этот момент может показаться несущественным, на самом деле
он важен. Всегда нужно достигать компромисса между быстрой разработкой
командного файла при согласии на недостатки жесткого синтаксиса - и
разрешением гибкого формата за счет дополнительных усилий по кодированию
и отладке и за счет более медленного выполнения. Ваш выбор зависит
от ваших приоритетов, от количества людей, использующих ваше инструментальное
средство, и от того, насколько критична скорость выполнения.
Конечно, если скорость критична, вы, вероятно, захотите использовать
каким-то образом язык Си. Мы оставляем обработку конкатенированных
опций в качестве упражнения для читателя.
Цикл for проходит по всем позиционным параметрам. Если первым
символом аргумента является "-", то он сверяется со списком допустимых
аргументов с помощью оператора case в строках 9-17. Опция "-l" изменяет
переменную формата, после чего убирается из рассмотрения. Это делается
для освобождения этой позиции, чтобы конечным результатом были
просто имена файлов в командной строке.
Опция "-s" также изменяет переменную формата. Однако, вместо того,
чтобы убрать опцию из командной строки, она ликвидирует всю командную
строку и заменяет ее символом "l". Это заставляет цикл for
проходить только одну итерацию, так как в командной строке теперь
только один параметр. Благодаря такому обращению с командной строкой,
нам не нужен другой цикл: мы можем использовать тот же цикл, что и в
определении маршрута, без всяких модификаций. Поскольку после опции s
не ожидается никаких имен файлов, мы больше не хотим рассматривать командную
строку.

Если использована опция, которая не является ни l, ни s, то этой
опции соответствует звездочка (*) и в стандартный файл ошибок выводится
сообщение об ошибке. Затем командный файл завершается.
Мы бы могли просто проверить первый параметр командной строки,
чтобы выяснить, является ли он опцией, и если является, то установить
эту опцию. Поскольку можно использовать только одну опцию за один раз,
мы могли бы предполагать, что в остальной части командной строки были
имена файлов. Тем не менее, этот цикл допускает простое добавление
других опций, которые могли бы действовать в дополнение к одной основной.
Это более предпочтительно, и оно не влияет на производительность.
В строке 21 мы добавляем символ двоеточия (:) к другим символам
разделителя полей. Мы должны именно добавить двоеточие, а не превратить
разделитель полей только в двоеточие. Если бы мы сделали последнее,
то это запутало бы разбор имен файлов в командной строке.
Основной цикл представлен в строках 23-40. Это двойной цикл for.
Внешний цикл проходит по каждому файлу в командной строке, а внутренний
цикл обрабатывает каждый каталог, указанный в вашей переменной
PATH. Обратите внимание, что внешний цикл идет по именам файлов, а не
по записям каталогов. Если бы мы выбрали второе, то в распечатке нарушился
бы порядок имен файлов, поскольку поиск шел бы сначала по каталогам.

Следовательно, для каждого имени файла и каталога действие зависит
от требуемого формата. Маршрутный формат печатает полное имя,
листинговый формат выполняет команду ls, а формат set не ищет указанные
имена файлов, но проверяет права доступа и ищет файлы с установленным
пользовательским идентификатором.
Обрат ПРИЕМЫ ПРОФЕССИОНАЛЬНОЙ РАБОТЫ В UNIX
аналогами. Опция ls есть дополнение, которое сокращает объем работы
при вызове. Наличие комбинации поиска и команды ls освобождает того,
кто вызывает этот командный файл от необходимости применять команду
подстановки. Старая и новая команды выглядят примерно так:

ll `path ll`
Находит путь к ll, а затем запускает на нем команду ls -l.

paths -l ll
Находит путь и вместо того, чтобы его напечатать,
выполняет команду ls -l применительно к этому пути.

Формат setuid в строке 34 прощается с подходом "один файл за один
раз" и включает каталоговую машину. Поскольку внешний цикл установлен
на одну итерацию, внутренний цикл становится главным. Для каждого каталога,
указанного в PATH, печатаются оформление из двоеточий и имя
каталога. Это делает распечатку приятной, информативной и наглядной.
Ключевой командой является комбинация ls-grep. Каждое имя файла в
каталоге распечатывается в длинном формате, затем просматривается бит
установки пользовательского идентификатора. Модель такова, что команда
ls -al $DIR печатает следующее:

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

|
| -rws--x--x 1 root bin 16235 Sep 13 1985 /bin/su
|
|

Аргумент "^[^ ]*s[^ ]*" означает поиск от начала строки символа,
отличного от пробела, за которым следует один или более символов, отличных
от пробела, затем символ s и затем один или более символов, отличных
от пробела. Это выражение ограничивает поиск битами прав доступа
в начале строки. Если имеется символ s где-либо в правах доступа
(либо в пользовательском идентификаторе процесса, либо в групповом
идентификаторе процесса), то команда grep отрабатывает успешно и печатается
вся строка.
Такой вид поиска установленного пользовательского идентификатора
несколько "легковесен" в том смысле, что поиск ведется только согласно
переменной PATH, которая у вас есть. Файлы с установленным пользовательским
идентификатором могут находиться в каталогах, которые не указаны
в PATH. Однако в такой реализации данная опция обеспечивает быстрое
обращение к вашим локальным файлам с установленным пользовательским
идентификатором.

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

Данный командный файл открыт для многих различных видов модификации.

Поиск полного имени файла является фундаментальной задачей программного
обеспечения по сопровождению файлов. Эта возможность позволяет
нам полагаться на саму программу paths или использовать paths в качестве
куска б

Список сторінок

Поділитися у соцмережах

☏ Замовлення реклами: +3805044-68-872

© Асоціація електронних бібліотек України

☝ Усі матеріали сайту (зокрема статті, зображення, рекламні оголошення тощо) призначені лише для попереднього ознайомлення. Усі права на публікації, представлені на сайті, належать їхнім законним власникам. Просимо Вас не зберігати копії інформації.