Жанр: Учеба

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

сторінка №4

(текущий каталог, что равносильно
команде "$ tree .") в качестве начала файлового дерева.

2. $ tree /

Печатает древовидный листинг для КАЖДОГО файла всей системы. Команда
find при таком ее запуске начинает с корневого каталога и выдает
информацию о всех файлах системы.

3. $ tree $HOME/..

Показывает древовидный формат для всех других пользователей
системы (предполагается, что все пользовательские каталоги находятся в
одном и том же каталоге, например /usr/*).

ПОЯСНЕНИЯ

Первая строка содержит только знак двоеточия (:) - "нулевую команду".
Это связано с тем, что все командные файлы, описываемые в этой
книге, сделаны так, чтобы их можно было запускать в среде интерпретатора
Bourne shell. Наш комментарий в строке 2, идентифицирующий
версию, начинается со знака решетки (#). Си-shell ищет этот знак как
первый знак командного файла. Если он найден, то предпринимается попытка
выполнить данный командный файл. В противном случае Си-shell передает
командный файл интерпретатору Bourne shell. Вот почему мы не
хотим начинать первую строку со знака #. Мы, конечно, могли бы оставить
первую строку чистой, но чистая строка невидима и может быть случайно
удалена. Соответственно мы будем использовать чистые строки в
других случаях, чтобы выделить важные участки программы.
Строка 2 идентифицирует версию. Символьная строка @(#) есть специальная
последовательность в строке комментария, которая распознается
как строка "what" ("что"). Команда what в системе UNIX читает файл и
печатает сообщение, которое следует за строкой "what". Чтобы идентифицировать
версию данного командного файла, наберите

# what tree

и будет напечатано следующее сообщение:

tree:
tree v1.0 Visual display of a file tree Author: Russ Sage

Строки 4-7 проверяют, не слишком ли много аргументов было передано
командной строке. Это осуществляется путем исследования переменной
$#, которая представляет собой счетчик числа позиционных параметров
командной строки. Если насчитывается более одного параметра, печатается
соответствующее сообщение об ошибке в стандартный файл ошибок
(stderr) и программа останавливается с плохим значением статуса.
Отметим, что команда echo обычно печатает в стандартный выход
(stdout). Мы можем перенаправить stdout в другой файловый дескриптор,
указав его. В данном случае мы собираемся печатать в stderr. Синтаксис
переводится так: "вывести эту строку и перенаправить ее в файловый
дескриптор (&) стандартного файла ошибок (2)". Печать сообщений об
ошибках в stderr обеспечивает согласованное поведение командного файла
независимо от среды, в которой он запущен.
Отметим также, что коды статуса выхода в интерпретаторе shell
противоположны тем, которые используются при программировании на языке
Си. В Си истинное значение есть 1, ложное отлично от 1. При программировании
на языке shell успешным статусом выхода (истиной) является 0,
а плохим статусом (ложью) ненулевое значение.
Вы, возможно, удивитесь, почему мы так беспокоимся о том, чтобы
вернуть ложный статус выхода, если командный файл собирается просто
напечатать сообщение об ошибке и прекратить работу. Дело в том, что
все инструментальные средства системы UNIX должны быть спроектированы
так, чтобы они могли быть связаны с другими командами и процессами, в
которые они могут быть встроены. Возможно, что другой команде необходимо
будет вызвать команду tree и проверить, корректно ли она отработала.
Хорошим стилем проектирования программных средств является прогнозирование
и разрешение многих способов использования программы.
Строки 9-15 проверяют, чтобы любые параметры, передаваемые командной
строке, были действительно каталогами, как указано в нашем
синтаксисе. Напомним, что в командной строке может быть помещен только
один каталог. Если мы используем только один параметр и этот параметр
не является каталогом, то мы печатаем сообщение об ошибке и выходим.
Таким образом, операторы проверки гарантируют, что либо не используется
ни один параметр, либо единственный используемый параметр является
корректным каталогом.

Мы подошли к сердцу команды tree - это строки 17-20. Главным
здесь является команда find системы UNIX. Каталог, в котором ведется
поиск, определяется при запуске команды. Синтаксис ${1:-.} является
формой параметрической подстановки и означает следующее: если $1 (что
является первым позиционным параметром) установлен (иными словами,
если аргумент был передан командной строке и был ненулевым), то нужно
использовать это значение. В противном случае следует использовать каталог
. (текущий каталог). Этот тип подстановки дает нам возможность
запускать команду tree без указания имени каталога (когда после tree
на командной строке ничего не следует),- то есть работать в режиме "по
умолчанию", что часто используется в различных файловых инструментах.
Команда find выводит на печать полное имя каждого файла, который
ей встречается. Поскольку не используется никакая специальная опция
для селекции файлов, печатаются все имена. После этого все полные имена
файлов сортируются для более удобного чтения. Такая сортировка
несколько увеличивает время работы команды, однако наглядность результата
говорит о том, что это время было потрачено с пользой.
Далее, отсортированные полные имена файлов передаются по программному
каналу команде sed системы Unix. Sed - это "потоковый редактор",
очень гибкое средство, которое может быть использовано для идентификации
и обработки различных образцов текста. Опции -e являются
операциями редактирования, применяемыми к поступающим данным. Первый
оператор просто сообщает команде sed, что нужно напечатать первую
строку, затем удалить строку 1. Это делается для того, чтобы напечатать
название корневого каталога, который исследуется. Этой строке не
требуется никакой дальнейшей модификации, так как корневой каталог не
имеет никаких дополнительных элементов путей доступа, которые нужно
было бы трансформировать в символы косой черты для показа отступов.
Удаление первой строки связано с тем, что она не нужна в дальнейшей
работе.
Вторая операция редактирования является командой подстановки. Она
заменяет каждый символ, отличный от символа косой черты (вплоть до
первого символа /) на последовательность пробелов и затем один символ
(/). Это избавляет нас от печатания имен промежуточных каталогов впереди
полного имени файла. Буква g в конце этой строки означает, что
эта операция выполняется глобально, то есть для всех считываемых символов.
Итак, теперь строка состоит из начального элемента пути и одной
или более последовательностей пробелов, разделенных символами косой
черты. Символы обратной косой черты (\) в конце операций редактирования
- это символы продолжения, которые сообщают команде sed, что нужно
продолжить работу со следующей строкой в текущем пакете операций редактирования.

Третья операция редактирования (строка 19) также является командой
подстановки и заменяет каждый символ, который не является пробелом
(вплоть до символа /) на "не символ" и один символ косой черты. Этот
оператор удаляет пробелы из предыдущего результата редактирования и
смещает символ в самую левую позицию. Это создает гнездовую индикацию,
которую мы видели в предыдущем примере.
Последняя операция редактирования (в строке 20) заменяет символ
косой черты и все отличные от него символы (до конца строки) просто на
символы, отличные от /. Отметим, что это устраняет самый правый символ
/, который присутствует в листинге команды find. В результате остается
имя подчиненного файла, сдвинутое вправо.
Отметим синтаксис \1 команды sed - признак, относящийся к первому
(в данном случае единственному) регулярному выражению в скобках, которое
ему предшествует. В данном случае команде sed указано пройти символы,
соответствующие регулярному выражению - символы, отличные от /.

2.1.2. thead - печать начала каждого файла

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

ИМЯ: thead
---------------------------------------------------------------------------

thеаd Печатает заголовок (первые несколько строк) файлов.

НАЗНАЧЕНИЕ

Пройти файловое дерево и напечатать первые несколько строк каждого
файла. Если не указан каталог, то thead действует как фильтр.

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

thead [dir...]

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

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

Печатает заголовки (первые несколько строк) всех моих исходных
файлов на языке Си.

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

1 :
2 # @(#) thead v1.0 Prints head of files in tree Author: Russ Sage
2а Печатает заголовки файлов в дереве

4 if [ "`echo $1|cut -c1`" = "-" ]
5 then echo "$0: arg error"
6 echo "usage: $0 [dir ...]"
7 exit 1
8 fi

10 case $# in
11 0) while read FILE
12 do
13 if file $FILE | fgrep text "/dev/null 2"&1
14 then echo "\n:::::::::::::::::::::"
15 echo " $FILE"
16 echo "\n:::::::::::::::::::::"
17 head -15 $FILE
18 fi
19 done;;
20 *) for NAME in $*
21 do
22 find $NAME -type f -print | sort | wile read FILE
23 do
24 if file $FILE | fgrep text "/dev/null 2"&1
25 then echo "\n:::::::::::::::::::::"
26 echo " $FILE"
27 echo "\n:::::::::::::::::::::"
28 head -15 $FILE
29 fi
30 done
31 done;;
32 esac

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

FILE Содержит имя каждого файла
NAME Имя каталога, заданное в командной строке

ОПИСАНИЕ

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

Как уже объяснялось ранее в этой главе, иерархическая файловая
система является очень значительной частью системы UNIX. Однако, только
несколько команд в UNIX имеют дело непосредственно с рекурсивным
поиском файлов. Единственный способ расширить возможности системы -
создать новые рекурсивные утилиты, работающие с файлами. В данном случае
мы соединим нашу стратегию поиска по дереву с командой head системы
UNIX для упрощения идентификации содержимого всех файлов в выделенном
сегменте файлового дерева.
Иногда у нас, возможно, будет возникать желание просмотреть файлы
в более чем одном каталоге. В больших проектах разработки программного
обеспечения файлы обычно создаются в нескольких иерархических каталогах.
Thead может работать со множеством путей доступа и выводить заголовки
(несколько первых строк файлов) в виде непрерывного потока.

ЧТО ДЕЛАЕТ thead?

Thead - это препроцессорная команда к команде head системы UNIX.
Команда head очень примитивна, но, добавляя к ней управляющую структуру
и логику, мы можем создать очень полезное инструментальное
средство, которого нет в стандартной среде UNIX.
Например, мы захотели просмотреть заголовки всех текстовых файлов
в нашем регистрационном каталоге. Если у нас имеется большое число
подкаталогов, нам необходимо все их пройти и просмотреть все файлы,
содержащиеся в них. Мы можем сделать это с помощью команды

$ thead $HOME

Если мы хотим просмотреть только исходные файлы на языке Си
(*.c), то представленный выше синтаксис не годится для этого. Он не
обладает достаточной гибкостью. Нам необходимо иметь способ указать
(или квалифицировать) файлы в $HOME перед тем, как просматривать их.
Так как команда thead может воспринимать полные имена файлов, мы можем
использовать следующую команду:

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

Команда find генерирует список файлов с расширением C, который
сортируется и подается по каналу на вход команде thead.
Как видно из представленных двух примеров, весьма полезной для
командного файла является возможность получать входные данные либо из
аргументов командной строки (как в первом примере), либо по программному
каналу (как во втором примере). Способность использовать программный
канал позволяет вам применять какие-либо другие команды системы
UNIX, которые могут отбирать входные данные для вашего командного
файла. Команда с такой двойной возможностью называется ФИЛЬТРОМ. Среди
стандартных команд системы UNIX вы найдете лишь несколько команд-фильтров,
таких как wc, awk, sort.
Эти два способа поступления входных данных в программы делают интерфейс
с командными файлами очень гибким. Мы можем подстраивать програмные
средства под наши нужды, а не подстраивать наши желания под
имеющееся программное обеспечение.
Аргументами для команды thead являются каталоги. Никаких опций,
начинающихся со знака "-" нет, только каталог или полные имена файлов.
Команда thead знает из синтаксиса, какой способ запуска команды будет
использоваться. Если командная строка содержит имя файла, thead
просмотрит все позиционные параметры. Если никакие имена не указаны,
thead читает стандартный ввод (stdin) и останавливается, когда встречает
EOF. (Такое бывает в случае, когда команда thead получает входные
из программного канала.)
Для каждого файла, с которым работает thead, выполняется контроль
- текстовый ли это файл. Применение команды head к исполняемым модулям
приводит к выводу "таинственных" символов на экран и иногда может вызвать
дамп оперативной памяти.

ПРИМЕРЫ

1. $ thead /etc

Печатает данные из каждого текстового файла, находящегося в каталоге
/etc. Очень полезная команда, так как большинство файлов в /etc
являются исполняемыми модулями. Удобно иметь возможность быстро изолировать
текстовые файлы.

2. $ thead /usr/include

Просматривает все подключаемые файлы (*.h), даже в системном подкаталоге
sys.

3. $ find $HOME -ctime 0 -print | thead

Ищет все файлы в вашем регистрационном каталоге, которые были изменены
в течении последних 24 часов. Для каждого файла проверяется,
текстовый ли он. Если файл текстовый, то он печатается.

ПОЯСНЕНИЯ

Строки 4-8 выполняют проверку ошибок. Так как команда thead не
имеет никаких опций, любые позиционные параметры, которые начинаются с
дефиса (-) являются неверными. Если первым символом первого позиционного
параметра оказывается "-", то печатается сообщение "argument
error" (ошибка аргумента) вместе с сообщением о способе запуска и команда
thead прекращает работу.
Некоторые приемы программирования для интерпретатора shell,
используемые в этих строках, довольно часто встречаются в данной книге,
поэтому имеет смысл остановиться на них подробнее.
Проанализируем строку 4, работающую изнутри наружу. Команда echo
выдает содержимое $1 (текущий параметр командной строки), которое передается
по программному каналу команде cut. Команда cut используется
для того, чтобы выделить определенные символы или группы символов из
строки. В данном случае опция -c1 используется для получения только
первого символа.


КОМАНДА cut ДЛЯ BSD

В системе BSD нет команды cut, но следующий командный файл все же
"вырезает" первое непустое поле в текущем аргументе.
Предположим, мы используем команду для генерации целого набора
строк. В данном случае это команда 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 находит все обычные файлы. В этой программе
нет никакой проверки на то, текстовый файл или нет, поскольку мы не
пытаемся напечатать все на экран. Поэтому мы ищем строки символов во
всех файлах, начиная от архивных и заканчивая исполняемым

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

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

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

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

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