find: Поиск Файлов по Некоторым Критериям

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

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

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

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

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

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

Вы все еще здесь? Замечательно, теперь немного попрактикуемся, потому что это лучший способ разобраться с этим монстром. Допустим, вы хотите найти все каталоги, находящиеся в каталоге /usr/share. Для этого введите:

find /usr/share -type d

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

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: если бы мы написали ( .. ) вместо \( .. \), shell интерпретировала бы их и попробовала выполнить -name "*.htm" -o -name "*.html" в другой sub-shell... Эту проблему можно было бы решить и по-другому - взять круглые скобки в двойные или одинарные кавычки, но обратная наклонная черта предпочтительней, так как в этом случае нужно изолировать только один символ.

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

-exec ln {} /home/httpd/obsolete \;

Здесь также нужно изолировать ;, иначе shell отинтерпретирует это как разделитель команд. Если вы этого не сделаете, то 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 "New images"

Вот так вот! Конечно, эта команда не очень удобна, если вы должны набирать её каждый раз, и вы хотели бы, чтобы это выполнялось регулярно... Это можно сделать так:



[14] Примечание : для корректной работы этого примера, каталоги /var/www и /var/www/obsolete должны находится в одной файловой системе!