find является одной из старейших утилит UNIX. Она предназначена для рекурсивного просмотра одного или более каталогов и нахождения в этих каталогах файлов, которые соответствуют некоторому набору критериев. При всей своей полезности эта утилита имеет не очень понятный синтаксис и требует некоторых усилий для усвоения. Общий синтаксис:
find [опции] [каталоги] [критерии] [действия] |
Если вы не укажете ни одного каталога, find будет искать в текущем каталоге. Если вы не укажете никаких критериев - это эквивалентно “true”, то есть будут найдены все файлы. Опции, критерии и действия настолько многочисленные, что мы здесь упомянем только некоторые из них. Давайте начнем с опций:
-xdev: не искать в каталогах, находящихся в других файловых системах;
-mindepth <n>: разрешить поиск файла глубже, чем n уровней вниз от указанного каталога;
-maxdepth <n>: искать файлы не глубже чем n уровней вниз от указанного каталога;
-follow: следовать по символическим ссылкам на каталоги. По умолчанию find не ходит по символическим ссылкам;
-daystart: при использовании проверок по времени (см. ниже), вместо значения по умолчанию (24 часа назад от текущего времени) за точку отсчета принимается начало текущего дня.
Критериями могут быть одна или несколько атомарных проверок. Вот некоторые полезные проверки:
-type <type>: поиск по типу файла; <type> может быть одним из: f (обычный файл), d (каталог), l (символическая ссылка), s (сокет), b (файл блочного типа), c (файл символьного типа) или p (именованный канал);
-name <образец>: Найти файлы, чьи имена содержат <образец>. При наличии этой опции, <образец> понимается как шаблон подстановки (см. главу “Шаблоны Подстановки Shell”);
-iname <pattern>: тоже самое что и -name, только регистронезависимо;
-atime <n>, -amin <n>: Ищутся файлы доступ к которым был произведен <n> дней назад (-atime) или <n> минут назад (-amin). Существует также возможность указать +<n> или -<n>, в этом случае будут найдены файлы, доступ к которым был произведен соответственно больше или меньше, чем <n> дней/минут назад;
-anewer <file>: Ищутся файлы, доступ к которым был раньше, чем к файлу <file>;
-ctime <n>, -cmin <n>, -cnewer <file>: тоже самое, что -atime, -amin и -anewer, но применимо к дате последней модификации, а не последнего доступа;
-regex <образец>: тоже самое, что и -name, но образец воспринимается как регулярное выражение;
-iregex <образец>: тоже самое, что и -regex, но не зависит от регистра.
Существует масса других атомарных проверок. Подробнее узнать о них можно, почитав страницы руководства find(1). Проверки можно комбинировать одним из следующих способов:
<c1> -a <c2>:(AND) true, если каждое <c1> и <c2> равны true; -a является неявным, поэтому вы можете его опускать и писать <c1> <c2> <c3> ... ;
<c1> -o <c2>:(OR) true, если любой из <c1> или <c2> или оба равны true. Примечание: -o имеет меньший приоритет, чем -a; для того чтобы выполнялся один из критериев <c1> или <c2> и обязательно критерий <c3>, вы можете использовать такую запись ( <c1> -o <c2> ) -a <c3>. Вы должны защитить (escape) (деактивировать) круглые скобки, иначе они будут интерпретироваться shell!
-not <c1>: инвертирует проверку <c1>, поэтому -not <c1> будет true в случае, когда <c1> равно false.
И наконец, вы можете определить действие для каждого найденного файла. Вот наиболее используемые:
-print: Выводит имена файлов на стандартный вывод. Это действие по умолчанию;
-ls: Выводит найденные файлы в стандартный вывод эквивалентно команде ls -ilds;
-exec <command>: выполняет команду <command> для найденных файлов. Командная строка <command> заканчивается символом ;, который должен быть защищен (деактивирован) для того, чтобы shell его не интерпретировала. Позиция в файле отмечается при помощи {}. В примерах по использованию (ниже) это хорошо проиллюстрировано;
-ok <command>: тоже самое что и -exec, но спрашивает подтверждения перед исполнением каждой команды.
Вы все еще здесь? Замечательно, теперь немного попрактикуемся, потому что это лучший способ разобраться с этим монстром. Допустим, вы хотите найти все каталоги, находящиеся в каталоге /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 должны находится в одной файловой системе!