строк. В данном случае это команда who:
for NAME in 'who | sed "s/^\([^ ]*\).*/\1/"'
do
done
Для каждой обнаруженной строки (аргумента) команда sed должна
подставить вторую строку символов вместо первой строки. Первая строка
- это строка, которая вырезается. Мы ищем от начала строки (^) символ,
отличный от пробела ([^ ]), за которым следует любое число непустых
символов (*). Эта операция прерывается по достижении пробела. Набор
непустых символов ограничивается обратными косыми чертами \( и \).
Впоследствии ссылка на этот набор дается в виде \1. Символы .* означа-
ют, что после того, как найден пробел, необходимо считать подходящими
все символы до конца строки. Мы находимся фактически сразу после того,
что заключено в пару символов \( и \). Группируя первый набор симво-
лов, отличных от пробела, мы получаем то, что является результатом ра-
боты команды "cut -f1".
В этом месте мы подходим к знакам ударения (`), окаймляющим все
выражение. Они берут результат работы всех команд, заключенных в знаки
ударения и передают на следующую охватывающую структуру в наших вло-
женных выражениях. Этот следующий уровень окаймления указан кавычками.
Кавычки превращают символ в строку, чтобы его можно было сравнить с
символом "-". Следующий слой - квадратные скобки, указывающие условие
для оператора if. Это приводит к тому, что генерируется нулевое (исти-
на) или ненулевое (ложь) условие, которое управляет тем, будет ли вы-
полнена часть then оператора if-then.
Мы не собираемся подробно анализировать много строк данного ко-
мандного файла, но мы хотим показать вам, как читать выражение или всю
строку текста программы так, чтобы это имело смысл.
Остальная часть командного файла представляет собой один огромный
оператор выбора (case). Аргументом, используемым для ветвления, явля-
ется число позиционных параметров в командной строке. Если позиционных
параметров нет, то в строках 11-19 активируется цикл while. Заметим,
что цикл while выполняет оператор чтения, но не указывает, откуда дол-
жен быть взят его вход. Это связано с тем, что входом по умолчанию яв-
ляется стандартный ввод (stdin). Для каждого имени файла, которое чи-
тается из стандартного ввода, запускается команда file системы UNIX.
Выход команды file передается по программному каналу команде fgrep (а
не grep, что увеличивает скорость), чтобы посмотреть, является ли файл
текстовым.
Фактический выход команды fgrep перенаправляется на нулевое уст-
ройство (в бесконечную область памяти), поскольку он нам не нужен.
Нас интересует лишь код возврата после выполнения всего конвейе-
ра. Если команды file и fgrep отработали успешно, кодом возврата явля-
ется ноль. Это истинное значение, поэтому выполняется участок цикла
после 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
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"