find - это одна из старейших утилит UNIX®. Она предназначена для рекурсивного сканирования одного или нескольких каталогов и поиска в них файлов, соответствующих определенному набору критериев. При всей своей полезности ее синтаксис не слишком понятен, и для ее использования требуется некоторая практика. Общий синтаксис:
find [опции] [каталоги] [критерий1] ... [критерийN] [действие]
Если вы не укажете ни одного каталога, find будет выполнять поиск в текущем каталоге. Если вы не укажете критерии, это будет эквивалентно «истине», т.е. будут найдены все файлы. Опции, критерии и действия настолько многочисленны, что здесь мы упомянем только некоторые из них. Вот некоторые опции:
-xdev
: не искать в каталогах,
находящихся в других файловых системах.
-mindepth <n>
: спускаться при
поиске файлов как минимум на n
уровней ниже указанного каталога.
-maxdepth <n>
: искать файлы не ниже
n
уровней относительно
указанного каталога.
-follow
: следовать по
символическим ссылкам, если они ссылаются
на каталоги. По умолчанию find не
переходит по символическим ссылкам.
-daystart
: при использовании
проверок, связанных со временем (см. ниже),
вместо значения по умолчанию (24 часа назад
от текущего времени) за точку отсчета
принимается начало текущего дня.
Критериями могут быть одна или несколько атомарных проверок. Вот некоторые полезные проверки:
-type <тип_файла>
: поиск файла
указанного типа.
<Типом_файла>
может быть:
f
(обычный файл), d
(каталог), l
(символическая
ссылка), s
(сокет), b
(файл блочного типа), c
(файл
символьного типа) или p
(именованный канал).
-name <шаблон>
: поиск файлов,
чьи имена соответствуют указанному
шаблону. В этой опции под шаблоном
подразумевается
подстановка имен
файлов (см. раздел Раздел 3, «Шаблоны подстановки в командном процессоре»).
-atime <n>
, -amin <n>
:
поиск файлов, обращение к которым был
выполнено n
дней назад
(-atime
) или n
минут
назад (-amin
). Вы также можете
указать опцию +<n>
или
-<n>
, в этом случае будет
выполнен поиск файлов, обращение к
которым было выполнено больше или меньше,
чем n
дней/минут назад.
-anewer <файл>
: поиск файлов,
обращение к которым было выполнено позже,
чем к файлу
.
-ctime <n>
, -cmin <n>
,
-cnewer <файл>
: эквивалент
-atime
, -amin
и
-anewer
, но применимо к дате
последнего изменения содержимого файла.
-regex <шаблон>
: эквивалент
-name
, но под шаблоном
подразумевается
регулярное
выражение.
Существует много других проверок. Для получения дополнительной информации обратитесь к странице руководства 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>
.
Вы должны
заэкранировать
(дезактивировать) круглые скобки, иначе
они будут интерпретированы 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
. Для этого нужно
сделать следующее[27]:
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, как показано в следующем разделе.
[27]
Обратите внимание, что в этом примере
требуется, чтобы каталоги
/var/www
и /var/www/obsolete
находились в одной файловой системе!