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

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

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

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

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

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

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

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

find /usr/share -type d

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

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, как показано в следующем разделе.



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