Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

find: Поиск файлов по определенным критериям

Prev 

Chapter 5. Утилиты командной строки

 Next


find: Поиск файлов по определенным критериям

find - это одна из старейших утилит UNIX®. Она предназначена для рекурсивного сканирования одного или нескольких каталогов и поиска в них файлов, соответствующих определённому набору критериев. При всей своей полезности её синтаксис не слишком понятен, и для её использования требуется некоторая практика. Общий синтаксис:

find [опции] [каталоги] [критерий] [действие]

Если вы не укажете ни одного каталога, find будет выполнять поиск в текущем каталоге. Если вы не укажете критерии, это будет эквивалентно “истине”, т.е. будут найдены все файлы. Опции, критерии и действия настолько многочисленны, что мы здесь упомянем только некоторые из них. Давайте начнем с опций:

  • -xdev: не искать в каталогах, находящихся в других файловых системах.
  • -mindepth <n>: спускаться при поиске файлов как минимум на n уровней ниже указанного каталога.
  • -maxdepth <n>: искать файлы не ниже n уровней относительно указанного каталога.
  • -follow: следовать по символическим ссылкам, если они ссылаются на каталоги. По умолчанию find не переходит по символическим ссылкам.
  • -daystart: при использовании проверок, связанных со временем (см. ниже), вместо значения по умолчанию (24 часа назад от текущего времени) за точку отсчета принимается начало текущего дня.

Критериями могут быть одна или несколько атомарных проверок. Вот некоторые полезные проверки:

  • -type <тип_файла>: поиск файла указанного типа. <тип_файла> может быть: f (обычный файл), d (каталог), l (символическая ссылка), s (сокет), b (файл блочного типа), c (файл символьного типа) или p (именованный канал).
  • -name <шаблон>: поиск файлов, чьи имена соответствуют указанному шаблону. В этой опции под шаблоном подразумевается универсализация имен файлов (см. главу «Шаблоны универсализации в командном процессоре»).
  • -iname <шаблон>: аналог -name, но без учета регистра.
  • -atime <n>, -amin <n>: поиск файлов, обращение к которым был выполнено n дней назад (-atime) или n минут назад (-amin). Вы также можете указать опцию +<n> или -<n>, в этом случае будут выполнен поиск файлов, обращение к которым было выполнено больше или меньше, чем n дней/минут назад.
  • -anewer <файл>: поиск файлов, обращение к которым было выполнено позже, чем к файлу.
  • -ctime <n>, -cmin <n>, -cnewer <файл>: аналог -atime, -amin и -anewer, но применимо к дате последнего изменения содержимого файла.
  • -regex <шаблон>: аналог -name, но под шаблоном подразумевается регулярное выражение.
  • -iregex <шаблон>: аналог -regex, но без учета регистра.

Существует много других проверок, для получения дополнительной информации обратитесь к странице руководства find(1). Проверки можно комбинировать одним из следующих способов:

  • <c1> -a <c2>: истина, если истинны оба выражения c1 и c2; опция -a является неявной, поэтому, если вам нужно проверить все выражения c1, c2 и c3, вы можете ввести <c1> <c2> <c3>.
  • <c1> -o <c2>: истина, если истинно любое из выражений c1 или c2. Обратите внимание, что опция -o имеет более низкий приоритет, чем -a, поэтому, если вам нужно найти файлы, удовлетворяющие критерию c1 или c2 и удовлетворяющие критерию c3, вам понадобится использовать скобки и ввести ( <c1> -o <c2> ) -a <c3>. Вы должны дезактивировать (escape) круглые скобки, иначе они будут интерпретированы shell'ом!
  • -not <c1>: инвертирует проверку c1, поэтому -not <c1> будет истиной, если <c1> - ложь.

И в заключение, вы можете указать действие для каждого найденного файла. Вот наиболее часто используемые:

  • -print: просто выводит имена файлов на стандартный вывод. Это действие по умолчанию.
  • -ls: для каждого найденного файла выводит на стандартный вывод эквивалент команды ls -ilds.
  • -exec <команда>: для каждого найденного файла выполняет <команду>. Командная строка <команда> должна заканчиваеться символом ;, который вы должны дезактивировать, чтобы shell его не интерпретировал; положение в файле отмечается при помощи {}. Смотрите примеры по использованию.
  • -ok <команда>: аналог -exec, но спрашивает подтверждение перед каждой командой.

Наилучшим способом разобраться со всеми опциями и параметрами будет рассмотрение примеров. Допустим, что вам нужно найти все каталоги в /usr/share. Для этого введите:

find /usr/share -type d

Предположим, что у вас есть HTTP-сервер, все ваши HTML-файлы находятся в каталоге /var/www/html, в котором вы в данный момент находитесь. Вам нужно найти все файлы, содержимое которых не изменялось в течение месяца. Поскольку эти страницы писали разные авторы, некоторые файлы имеют расширение html, а некоторые - htm. Вам нужно поместить ссылки на эти файлы в каталог /var/www/obsolete. Для этого нужно сделать следующее[17]:

find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 \
-exec ln {} /var/www/obsolete \;

Этот пример несколько сложноват и требует небольшого пояснения. Критерий поиска следующий:

\( -name "*.htm" -o -name "*.html" \) -a -ctime -30

он делает то, что нам нужно - находит все файлы, имена которых заканчиваются на .htm или .html\( -name "*.htm" -o -name "*.html" \) ”, и (-a) те файлы, которые не были изменены на протяжении последних 30 дней или, грубо говоря, месяца (-ctime -30). Обратите внимание на скобки: здесь они необходимы потому, что опция -a имеет более высокий приоритет. Если бы они отсутствовали, были бы найдены все файлы, заканчивающиеся на .htm, плюс все файлы, заканчивающиеся на .html, которые не были изменены в течение месяца, а это не то, что нам нужно. Также обратите внимание, что круглые скобки дезактивированы для shell'а: если бы мы ввели ( .. ) вместо \( .. \), командный процессор интерпретировал бы их и попытался выполнить -name "*.htm" -o -name "*.html" в sub-shell'e... Другое решение - заключить круглые скобки в двойные или одинарные кавычки, здесь предпочтительней использовать обратную косую черту,т.к. нам нужно изолировать только один символ.

И, наконец, вот команда, которая будет выполнена для каждого файла:

-exec ln {} /var/www/obsolete \;

Здесь вы также должны дезактивировать знак ;, в противном случае командный процессор интерпретирует его как разделитель команд. Если вы забудете сделать это, find пожалуется, что для -exec отсутствует аргумент.

Последний пример: у вас есть огромный каталог (/shared/images), содержащий все виды изображений. Вы регулярно используете команду touch для обновления в этом каталоге временной метки у файла с именем stamp с тем, чтобы иметь привязку ко времени. Вам нужно найти все изображения JPEG более новые, чем файл stamp, но поскольку вы получали изображения из различных источников, эти файлы имеют расширения jpg, jpeg, JPG или JPEG. Вы также хотите избежать поиска в каталоге old. И вам нужно, чтобы этот список файлов был отправлен к вам по почте, а ваше имя пользователя - peter:

find /shared/images -cnewer     \
     /shared/images/stamp       \
     -a -iregex ".*\.jpe?g"     \
     -a -not -regex ".*/old/.*" \
       | mail peter -s "Новые изображения"

Конечно, эта команда не слишком полезна, если вы каждый раз должны набирать её, и вы бы предпочли, чтобы она выполнялось регулярно. Простым способом периодического запуска команды является использование демона cron, как показано в следующем разделе.



[17] Обратите внимание, что в этом примере требуется, чтобы каталоги /var/www и /var/www/obsolete находились в одной файловой системе!


Prev 

Up

 Next

Операции с файлами и фильтрация 

Home

 Запуск команд по расписанию