7.6. Поиск файла по заданным атрибутам

[+]7.6.1. Условия для поиска командой find(1)
[+]7.6.2. Действия выполняемые командой find(1) с найденными файлами
[+]7.6.3. Связка с командой xargs

Описание:  Утилита find(1) незаменима при поиске файлов с заданными параметрами. Кандидат BSDA должен свободно владеть ею. На экзамене его могут попросить найти файл с заданным последним временем изменения, размером, типом, файловыми флагами, UID, GID, пермиссиями или содержащим в названии какой-то шаблон.

Практика: find(1)

Комментарий

Команда find(1) предназначена для поиска файлов по заданному критерию. Формат команды:

find [опции] где_искать [что искать (условия)] [действие]
        

Опции могут быть различные, они регулируют поведение find(1) при поиске. К тому же они в разных версиях и в разных системах разные. Так, напрмер, опция -E, изменяющая синтаксис регулярных выражений в опциях -regex и -iregex подобно аналогичной опции в grep(1), имеется только в FreeBSD.

Пожалуй важно знать опцию -x. Она предотвращает поиск на устройствах отличных от устройства, с которого поиск начался. Например:

find -x /home/user
        

Эта команда выведет список всех файлов в домашнем каталоге пользователя user и во всех подкаталогах, кроме каталогов являющихся точками монтирования других устройств (/home/user/mnt/floppy, /home/user/mnt/cdrom, /home/user/mnt/nfs).

Путь надо указывать обязательно. Некоторые версии GNU find проводят поиск относительно текущего каталога, если им не указать путь, find в BSD требует явного указания пути. В особых случаях, когда путь, в котором надо производить поик имеет необычное имя, можно указать его как аргумент опции -f.

7.6.1. Условия для поиска командой find(1)

Условия для поиска задаются опциями, перечисленными ниже. При задании нескольких опций, они объединяются через оператор «и». Связку «и» можно задать явно используя опцию -and. Связка «или» задаётся опцией -or. Унарное отрицание — ! и символы группировки — (...), в sh и csh необходимо защищать используя либо кавычки, либо \.

-amin n -cmin n -mmin n
Условие истинно, если разница между временем когда был запущен поиск и временем файла меньше n минут. Здесь под временем файла имеется ввиду: -amin — access time (время последнего доступа к файлу), -cmin — change of file status information (время изменения информации о состоянии файла) -mtime — modification time (время изменения файла)
-anewer file -cnewer file -mnewer file
Условие истинно, найденный файл моложе указанного. Здесь под временем файла имеется ввиду: -anewer — access time (время последнего доступа к файлу), -cnewer — change of file status information (время изменения информации о состоянии файла) -mnewer — modification time (время изменения файла)
-atime n -ctime n -mtime n

Условие истинно, если разница между временем когда был запущен поиск и временем файла округлённая вверх, в точности n суток. Здесь под временем файла имеется ввиду: -amin — access time (время последнего доступа к файлу), -cmin — change of file status information (время изменения информации о состоянии файла) -mtime — modification time (время изменения файла)

У времени можно задавать знак: -mtime -7 — ищем файлы изменённые за последнюю неделю, -mtime +7 — ищем файлы не менявшиеся в течение недели.

В FreeBSD имеется расширение синтаксиса: опциям -atime, -ctime и -mtime можно указывать время с размерностью в секундах, минутах, часах, днях и неделях. Например 2s, 3m, 4h, 5d, 6w. Можно комбинировать размерности: 5d6w — шесть недель и пять дней.

В OpenBSD и NetBSD для аналогичного поиска придётся пользоваться опциями -amin, -cmin и -mmin, переводить время в минуты и использовать где надо отрицание !.

-newer file
Условие истинно, если найденный файл изменён позже указанного
-newerXY file

Опция есть только в FreeBSD!

Условие истинно, если найденный файл имеет большее время доступа (X=a), изменения статуса (X=c), модификации (X=m) указанного чем время доступа (Y=a), изменения статуса (Y=c), модификации (Y=m) у указанного файла.

-empty
Условие истинно, если найденный файл или каталог пуст
-flags [-] flags
Условие истинно, если найденный файл или каталог имеет указанные флаги. Флаги являются свойством файловой системы UFS и описаны в справочной странице chflags(1). Если знак - не указан, условие истинно, если флаги в точности соответствуют указанным. Если знак - указан, условие совпадает, если как минимум указанные флаги присутствуют.
-perm [-] mode
mode может быть как символьным (chmod), так и восьмеричным. - имеет то же значение, что и в опции -flags.
-group group, -user user
Условие истинно, если найденный файл принадлежит пользователю user или группе group. Можно указавать как имена, так и ID (если указано чисто и пользователя с таким именем нет, оно толкуется как ID).
-nogroup, -nouser
Условие истинно, если найденный файл принадлежит неизвестному пользователю или неизвестной группе.
-type type

Условие истинно, если найденный файл имеет указанный тип. Возможные типы файла:

bblock specialблочное устройство
ccharacter specialсимвольное устройство
ddirectoryкаталог
fregular fileобычный файл
lsymbolic linkсимвольная ссылка
pFIFOименованный канал
ssoketсокет

-fstype type
Условие истинно, если найденный файл находится на устройстве с файловой системой типа type. Возможные типы файловых систем можно узнать при помощи sysctl(8) (sysctl vfs). Кроме того, можно использовать ключевые слова local и rdonly.
-[i]name pattern
Имя файла. Можно указать шаблон с *, ? и []. Опция -iname отличается тем, что игнорирует регистр.
-[i]path pattern
Путь к файлу. Можно указывать шаблон с *, ? и []. / считается обычным символом. Вариант нечувствителоьный к регистру реализован в FreeBSD и NetBSD.
-[i]regex pattern

Опция не реализована в OpenBSD!

Условие истинно, если имя файла (вместе с путём) полностью соответствует регулярному выражению pattern. Что значит полностью? Шаблон foo не соответствует пути ./foo/bar, а .*foo.* соответствует. Вариант -iregex нечувтвителен к регистру.

-size n[c]
Условие истинно, если файл занимает n блоков по 512 байт. Внимание! команда find(1) не использует переменную окружения BLOCKSIZE. Если указан суффикс c, то размер берётся точно в байтах. Так же как и в случае с опцией -atime можно задавать знак: -size -1024c — искать файлы размером меньше килобайта.
-maxdepth n и -mindepth n
Максимальная и манимальная глубина погружения в подкаталоги. Например: -maxdepth 1 искать файлы только в текущем каталоге, не погружаясь в подкаталоги.

7.6.2. Действия выполняемые командой find(1) с найденными файлами

Простейшее действие — напечатать имя файла на STDOUT. Это действие по умолчанию предпринимается если ничего другого не указано. Оно эквивалентно указанию опции -print.

-print
Действие по умолчанию. Напечатать имя файла.
-print0
Напечатать имя файла. Имена при этом будут разделяться нулевым символом. Опция сделана для совместной работы с командой xargs(1).
-ls
Про каждый файл выводится подробная информация. Так, как будто к файлам применили команду ls -dgils.
-delete

Опция есть только в FreeBSD!

Удалить найденные файлы и директории.

-exec команда [опции [{}]] ;
Наиболее общий случай. Позволяет применить к найденным файлам произвольную команду. Опции будут переданы этой команде. Если в опциях встретится пара скобок {}, то она будет заменена именем файла (с путём). Символ ; означает, что в этом месте закончилась команда и её опции.
-execdir команда [опции [{}]] ;
То же, что и -exec, но при выполнении команды find(1) переходит в каталог, где находится файл, а {} заменяет на имя файла без пути.
-ok команда [опции [{}]] ;
То же, что и -exec, но перед выполнением команды find(1) запрашивает подтверждение у пользователя.
-exec команда [опции [{}]] +

Опция есть только в FreeBSD!

Эквивалентна опции -exec ... ;, но замещает {} не одним найденным файлом, а списком файлов. Таким образом, указанная команда вызывается лишь однажды. Это работает как же, как связка с командой xargs(1).

7.6.3. Связка с командой xargs

xargs [опции] команда с аргументами
        

Команда xargs(1) читает список файлов из своего STDIN и подставляет эти файлы в качестве аргумента команде указываемой ей в аргументе. Её удобно применять в связке с find(1). Например, пусть нам надо узнать сколько строк в каком из журнальных файлов на нашей системе.

$ find /var/log \! -name *bz2 -type f | xargs wc -l
     875 /var/log/auth.log
     125 /var/log/cron
     270 /var/log/debug.log
     .....
        

Знак \ перед ! мы поставили для того, чтобы sh не интерпретировал его по-своему. Аналогичного результата мы могли бы добиться используя опцию -exec.

$ find /var/log \! -name *bz2 -type f -exec wc -l {} \;
     875 /var/log/auth.log
     125 /var/log/cron
     270 /var/log/debug.log
     .....
        

Даже на современной, быстродействующей машине разница в выполнении этих двух команд видна невооружённым взглядом. Мы можем её измерить:

$  time find /var/log \! -name *bz2 -type f | xargs wc -l > /dev/null

real    0m0.032s
user    0m0.000s
sys     0m0.024s

$ time find /var/log \! -name *bz2 -type f -exec wc -l {} \; > /dev/null

real    0m0.242s
user    0m0.040s
sys     0m0.191s
        

Разница составила целый порядок. А ведь здесь ещё мало файлов. В чём же дело? А дело в том, что find(1) с опцией -exec для каждого найденного файла заново вызывает команду wc(1). Вызов команд в UNIX (и не только в нём) — очень дорогая процедура. В случае с использованием xargs(1), мы вызвали wc(1) только один раз. Подсчёт статистики связанный с чтением и анализом содержиного файла более прост, чем сам вызов wc(1).

Ещё очень полезной особенностью xargs(1), является то, что он может разбирать список файлов с STDIN, считая символом разделителем не пробел, а нулевой символ, которого в принципе не может быть в имени файла. Это может быть очень важно, например, при работе с разделами Windows или Macintosh.

$ find /mnt/samba ... -print0 | xargs -0 ...
        

Теперь, какие бы причудливые имена ни встретились нам, даже если они содержат кавычки, пробелы, символы конца строки, они будут обработаны корректно.