Программирование в стандарте POSIX


Обход и обработка файловых иерархий


Для обхода файловой иерархии и систематической обработки ее элементов служит утилита find:

find [-H | -L] файл ... [выражение]

Она рекурсивно обходит каждую из иерархий с корнями в заданных файлах (разумеется, обычно в этом качестве указываются каталоги), отыскивая файлы, которые удовлетворяют логическому выражению, построенному с помощью описанных ниже средств. Опции -H и -L стандартным образом влияют на трактовку символьных ссылок. Если указуемый файл не существует, find оперирует с самой ссылкой.

Перечислим элементарные логические выражения и правила их вычисления. (Далее n обозначает целое десятичное число; вместо него могут указываться также комбинации +n и -n, что означает, соответственно, "больше, чем n" и "меньше, чем n".)

-name шаблон_файлов

Истина, если имя текущего файла удовлетворяет шаблону_файлов. Символы шаблона, имеющие для shell специальный смысл, должны быть экранированы.

-type тип_файла

Истина, если файл имеет заданный тип: b, c, d, f, p или s - является, соответственно, блочным или символьным специальным файлом, каталогом, обычным файлом, каналом или сокетом.

-size n[c]

Истина, если файл занимает n блоков по 512 байт. Когда указана буква c, размер файла задается в символах. Напомним, что с помощью комбинаций +n и -n можно проверять размер (и три указанные ниже характеристики) не только на равенство, но и на неравенство.

-atime n

Истина, если последний доступ к файлу производился n дней назад (в данном контексте день - это промежуток времени в 86400 секунд).

-mtime n

Истина, если файл последний раз модифицировался n дней назад.

-ctime n

Истина, если атрибуты файла последний раз изменялись n дней назад.

-perm [-]режим

Истина, если режим файла соответствует заданному. При наличии знака минус соответствие понимается как включение (все заданные биты должны присутствовать в режиме доступа к файлу); если минус отсутствует, требуется точное совпадение. Режим задается аналогично утилите chmod.

-links n

Истина, если на файл имеется n жестких ссылок.

-user имя_пользователя

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

-nouser

Истина, если идентификатор владельца файла отсутствует в базе данных пользователей.

-group владеющая_группа

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

-nogroup

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

-depth

Всегда истина; задает дисциплину обхода иерархии вглубь: сначала обрабатываются все элементы каталога, потом - сам каталог (по умолчанию find в первую очередь обрабатывает каталог и лишь после этого - его элементы).

-xdev

Всегда истина; предписывает не спускаться в каталоги, имеющие другой идентификатор устройства (st_dev, см. выше описание структуры stat).

-prune

Всегда истина; предписывает не обрабатывать текущий файл, если он является каталогом.

-exec команда

Истина, если после выполнения команды возвращается нулевой код завершения. запись команды должна заканчиваться экранированной точкой с запятой. Аргумент команды, заданный в виде пары фигурных скобок {}, заменяется маршрутным именем обрабатываемого файла.

-ok команда

Эквивалентно -exec за исключением того, что перед выполнением команды запрашивается подтверждение (в виде сгенерированной командной строки со знаком вопроса в конце), и она выполняется только при ответе y.

-print

Всегда истина; вызывает выдачу маршрутного имени обрабатываемого файла на стандартный вывод. Если в командной строке find не задано выражение, то подразумевается -print. Если выражение не содержит ни -exec, ни -ok, ни -print, вместо него используется конструкция( выражение ) -print

-newer файл

Истина, если текущий файл был модифицирован позднее указанного файла

( выражение )

Истина, если истинно заключенное в скобки выражение (скобки должны быть экранированы от интерпретации языком shell).

Элементарные логические выражения могут комбинироваться с помощью следующих операций (в порядке уменьшения приоритета):

  • унарная операция отрицания, обозначается !.
  • логическое И, обозначается пробелом или -a. Если значением первого операнда оказалась ложь, второй не вычисляется. Таким образом, последовательность разделенных пробелами выражений-операндов можно рассматривать как составной фильтр, через который пропускается текущий файл: если значением очередного операнда оказалась ложь, обработка прекращается; в противном случае файл передается следующему компоненту фильтра.

  • логическое ИЛИ, обозначается -o. Если значением первого операнда оказалась истина, второй не вычисляется.

Приведем несколько примеров. Пусть нужно подсчитать число C-файлов в текущем каталоге и его подкаталогах. Воспользуемся следующим конвейером:

find . -name \*.c -print | wc -l

Обратим внимание на то, что команда find - одна из немногих, интерпретирующая шаблоны имен файлов самостоятельно. Собственно, у нее нет другого выхода, поскольку она должна рекурсивно обойти указанные иерархии и в каждой из них искать файлы, имена которых удовлетворяют заданному шаблону. Нетрудно заметить, что стандартный shell-механизм генерации имен файлов в данном случае не годится.

Рассмотрим более сложный пример. Нужно подсчитать суммарное число строк во всех C-файлах текущего каталога и его подкаталогов. Решений может быть несколько, рассмотрим три из них. Во-первых, можно организовать конвейер:

cat `find . -name \*.c -print` | wc -l

Утилита find отберет имена нужных файлов, cat выдаст их совокупное содержимое на стандартный вывод, а команда wc -l подсчитает общее число строк. По существу ту же идею можно выразить в другой форме:

find . -name \*.c -exec cat {} \; | wc -l

Здесь содержимое нужных файлов будет выдаваться на стандартный вывод по мере их обнаружения.

Наконец, можно поступить совсем просто, сделав нужные файлы аргументами команды wc. Правда, при этом будет выдаваться еще и число строк в каждом из C-файлов:

wc -l `find . -name \*.c -print`

Напишем теперь команду, позволяющую удалить из текущего каталога и его подкаталогов все файлы нулевого размера, а также объектные файлы, к которым не было доступа более месяца, запрашивая подтверждение (см. пример 4.51). Обратим внимание на пробелы, окружающие экранированные скобки и экранированную точку с запятой.

find . \( -size 0c -o -name \*.o -a -atime +30 \) -ok rm {} \;

Листинг 4.51. Еще один пример использования утилиты find.

Пусть на каталог   /mnt   смонтирован съемный носитель. Требуется выявить на нем файлы, владелец или группа которых на данной системе неизвестны, и изменить их на nobody. К цели ведет команда, показанная в пример 4.52 (предполагается, что тот, кто ее выполняет, обладает соответствующими привилегиями).

find /mnt \( -nouser -o -nogroup \) -exec chown nobody:nobody {} \;

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

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

Две команды, показанные в пример 4.53, почти эквивалентны: обе не заходят в подкаталоги с именем skip   текущего каталога, но только вторая выводит их имена.

find . -name skip -prune -o -print find . -print -name skip -prune

Листинг 4.53. Пример использования элементарного выражения -prune.

Одной из форм обхода и обработки файловой иерархии можно считать архивирование. Стандарт POSIX предусматривает для этого служебную программу pax:

pax [опция ...] [шаблон ...] pax -r [опция ...] [шаблон ...] pax -w [опция ...] [файл ...] pax -r -w [опция ...] [файл ...] каталог

Утилита pax поддерживает несколько архивных форматов, в число которых входят "родной" pax и унаследованные cpio и tar. В зависимости от заданной комбинации опций -r и -w pax осуществляет одну из четырех операций: выводит оглавление архива (первая из приведенных выше форм команды pax), читает архив и извлекает из него файлы по заданным шаблонам имен (вторая форма), записывает заданные файлы в архив (третья форма) или копирует иерархии файлов (четвертая форма).

Как правило, архив читается со стандартного ввода, а записывается на стандартный вывод; если встречается файл   типа"каталог", обработке подвергается вся иерархия файлов с корнем в этом каталоге.

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

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

В пример 4.54 представлены примеры использования команды pax. Первая архивирует содержимое текущего каталога, последняя копирует каталог   old_dir в new_dir.

pax -w . mkdir new_dir pax -rw old_dir new_dir

Листинг 4.54. Пример использования служебной программы pax.

<


Начало  Назад  Вперед