Поиск в unix. Часть 1


Dru Lavigne
перевод Станислава Лапшанского
Впервые опубликован: http://www.computerra.ru.


Программа поиска "find" является одной из самых полезных утилит, всегда находящихся в любой UNIX-системе.

В этой статье я расскажу вам о синтаксисе команды find и продемонстрирую ее применение на нескольких практических примерах.

Сама по себе команда find имеет очень простой синтаксис:
find где_искать ключи

Часть команды, которую мы назвали "ключи", на первых порах может показаться вам запутанной. К тому же ее конкретная запись может варьироваться в зависимости от той версии UNIX, которой вы пользуйтесь в данный момент. Если вы работаете на новой для вас системе, то вероятно вам придется заглянуть на страницу руководства, посвященную команде find (man find - прим. переводчика). Вот список самых основных ключей, которые вы можете использовать во FreeBSD:

Ключ Назначение
-name искать по имени файла, при использовании подстановочных образцов параметр заключается в кавычки
-type тип искомого: f=файл, d=каталог, l=связь (link)
-user владелец: имя пользователя или UID
-group владелец: группа пользователя или GID
-perm указываются права доступа
-size размер: указывается в 512-байтных блоках или байтах (признак байтов - символ "c" за числом)
-atime время последнего обращения к файлу
-ctime время последнего изменения владельца или прав доступа к файлу
-mtime время последнего изменения файла
-newer искать файлы созданные позже указанной даты
-delete удалять найденные файлы
-ls генерирует вывод как команда ls -dgils
-print показывает на экране найденные файлы
-exec command {} ; выполняет над найденным файлом указанную команду; обратите внимание на синтаксис
-ok перед выполнением команды указанной в -exec, выдает запрос
-depth начинать поиск с самых глубоких уровней вложенности, а не с корня каталога
-prune используется, когда вы хотите исключить из поиска определенные каталоги


Я покажу вам на примерах, как использовать и сочетать эти ключи при поиске. Однако перед этим давайте поглядим, для чего может использоваться команда find. Если вы воспользуйтесь для этого командой whatis, то результат ее работы может вас удивить:
whatis find
find(1) -- walk a file hierarchy (обход иерархии файлов)

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

Давайте начнем с нескольких простых примеров, для того что бы потом перейти к более сложным. Самый простой поиск вы можете провести, напечатав следующее:
find . -print

Поскольку символ точки "." означает текущий каталог, то эта команда найдет и напечатает (-print) на терминале все файлы находящиеся в текущем каталоге, а так же во всех подкаталогах.

В FreeBSD ключ "-print" подразумевается по умолчанию, так что даже если вы забудете его написать, вы получите такой же результат:
find .

Однако будет правильнее, если вы выработаете в себе привычку писать "-print", так как на многих UNIX-системах "-print" не является подразумеваемым выражением.

Для того что бы найти все файлы в вашем домашнем каталоге, сначала вы должны увериться в том, что находитесь именно там, а затем повторить команду поиска:
cd
find . -print

Команда cd выполненная без параметров вернет вас в ваш домашний каталог. Поскольку команда find может использоваться для выполнения серьезных действий со значительными последствиями, всегда будет правильным сначала перейти в тот каталог, в котором вы хотите производить эти действия. В этой статье я подразумеваю, что вы находитесь в вашем домашнем каталоге. Итак, чтобы выполняемые действия не нанесли необратимых изменений в вашей системе, не покидайте домашний каталог.

Приведенные примеры показали, как просто использовать команду find. Однако скорее всего вы хотите найти нечто конкретное, когда обращаетесь к find. С этого места в игру вступают другие ключи команды find. Попробуем найти файл по его имени:
touch file1
find . -name file1 -print
./file1

Давайте разберемся, что я сейчас сделала. Я создала пустой файл с именем file1 при помощи команды touch. Затем я приказала find поискать в моем текущем каталоге (".") файл с именем ("-name") file1 и напечатать ("-print") результат поиска на экране. Теперь я могу сказать, что в моем домашнем каталоге и содержащихся в нем подкаталогах, есть лишь один файл с именем file1, так как команда find выдала одну строку с именем найденного файла.

Обычно, мы прибегаем к помощи команды find, когда нам необходимо найти более одного файла. Например вам может понадобиться найти все файлы с определенным расширением. Я имею привычку скачивать большое количество pdf-файлов и, при этом, часто забываю сохранять их в каком-то определенном месте. Иногда я складываю эти файлы в каталог с названием pdfs. Когда меня посещает такое желание, я использую команду find для поиска pdf-файлов во всех подкаталогах моего домашнего каталога:
find . -name "*.pdf" -print
./pdfs/50130201a.pdf
./pdfs/50130201b.pdf
./pdfs/50130201c.pdf
./pdfs/IWARLab.pdf
./pdfs/DoS_trends.pdf
./pdfs/Firewall-Guide.pdf
./2000_ports.pdf

Это значит, что я была аккуратна последнее время, так как только один файл не находится в "правильном" подкаталоге.

Заметьте, что для того, чтобы команда find работала, я заключила образец *.pdf в кавычки, написав "*.pdf", а не просто *.pdf. Существуют еще два способа правильного написания образцов. Эти две команды дадут одинаковый результат:
find . -name *.pdf -print
find . -name '*.pdf' -print

Давайте добавлять к этой команде другие команды и смотреть, что в результате получится. Что если нас интересуют только те файлы, которые находятся не в каталоге pdfs? Давайте повторим команду find, но в данном случае переправим ее вывод команде grep, в результате чего мы получим только одно имя:
find . -name "*.pdf" -print | grep -v "^./pdfs/"
./2000_ports.pdf

Отлично, команда работает, однако ее синтаксис выглядит устрашающе. Разберем его по частям. Когда вы используете grep -v, вы создаете инвертированный фильтр. Это значит, что grep будет выбирать из входного потока все строки, в которых НЕ содержится указанный образец. В моем случае, меня не интересуют файлы, которые находятся в каталоге ./pdfs/, я исключаю их используя инверсный фильтр. Обратите внимание, что выражение записано в кавычках. Я так же добавила в фильтр параметр ^. Символ степени "^" говорит команде grep, что образец надо сравнивать только с началом строки. Обратный слеш "" нужен для того, что бы команда grep не воспринимала точку "." как специальный символ. Сложенный вместе, такой образец скажет команде grep, показать мне файлы которые не лежат в каталоге ./pdfs/. Вот таким образом я получила желаемый результат.

Давайте наберемся смелости и сделаем что-нибудь более полезное, чем просто просмотр списка файлов. Попробуем использовать find для того, чтобы не просто найти необходимые файлы, но и переместить их в правильный каталог:
find . -name "*.pdf" -print | grep -v "^./pdfs/" | xargs -J X mv X ./pdfs/

Для того чтобы убедиться в том, что это сработало, выполним первоначальную команду find:
find . -name "*.pdf" -print
./pdfs/50130201a.pdf
./pdfs/50130201b.pdf
./pdfs/50130201c.pdf
./pdfs/IWARLab.pdf
./pdfs/DoS_trends.pdf
./pdfs/Firewall-Guide.pdf
./pdfs/2000_ports.pdf

Посмотрим, каким образом работает эта конструкция. В то время как grep заканчивает фильтрацию вывода команды find, мы передаем результаты его работы команде xargs, для завершения операции. Ключ J говорит xargs взять все полученные строки и подставить их в указанную команду. Например, перед вызовом команды find я не знала, сколько файлов придется перемещать - такой файл может быть один, или их может быть несколько. Вне зависимости от того, сколько файлов будет найдено, мне необходимо, чтобы все они были перемещены в каталог pdfs. Это сделать нам поможет маленькая магия ключа J. Для того чтобы ключ J сработал правильно, я определила символ "X" и поместила его по обоим сторонам команды mv. (Я честно перевел это, вы, я думаю, честно прочитали. На самом деле так никто не делает. Во второй части этой статьи вы узнаете, как пользоваться ключом "-exec", который как раз и предназначен для подобных действий - прим. переводчика).

Помните, что имена файлов в системе UNIX не обязательно имеют расширения. Поэтому вам может понадобиться задать более сложный образец для поиска. Скажем, я захотела найти все файлы, содержащие в своем имени подстроку "bsd". Для этого следует воспользоваться следующей командой:
find . -name "*bsd*" -print
./.kde/share/icons/favicons/www.freebsd.org.png
./.kde/share/icons/favicons/www.freebsddiary.org.png
./.kde/share/wallpapers/bsdbg1280x1024.jpg
./mnwclient-1.11/contrib/freebsd


Мы можем искать файлы не только по имени. Например, найдем файлы, которые не читали более чем (условие "более чем" задается знаком "+") 30 дней:
find . -atime +30 -print

Для поиска файлов, которые вы не модифицировали какое-то время, используйте ключ "-mtime", а для файлов, у которых определенное время назад не менялись права доступа, используйте "-ctime". Число после символа "+" задает количество дней (суток). Для того, чтобы узнать какие файлы были модифицированы сегодня, попробуйте:
find . -mtime -1 -print


Эта команда покажет вам, какие файлы были модифицированы за последние сутки. Заметьте, что для указания времени "меньше чем" следует указать знак "-".

Ключ "-newer" - это еще один ключ, имеющий отношение ко времени. Все три ключа "time" в качестве параметра используют суточные интервалы. Если вы хотите использовать более точные интервалы времени, то вам может помочь ключ "-newer", который позволяет сравнивать время модификации, последнего чтения или смены прав одного файла с другим с точностью до минуты. Например для того чтобы поглядеть, какие скрытые (файлы с точкой в начале) были изменены с тех пор, как вы последний раз изменили файл .cshrc, вы должны выполнить такую команду:
find . -type f -name ".*" -newer .cshrc -print

Обратите внимание, что мы включили в эту команду еще один незнакомый ключ. "-type" с параметром "-f", который устанавливает тип искомого "файл", так как в данном случае нам не нужны изменившиеся каталоги. Ключ "-name" будет искать файлы с именем, начинающимся на точку, а ключ "-newer" отберет файлы, которые модифицировались позже файла .cshrc.

По умолчанию подразумевается, что между ключами поиска стоит логический оператор "И", если вы хотите, чтобы вместо "И" использовался "ИЛИ", укажите ключ "-o". Поскольку между ключами по умолчанию подразумевается логический оператор "И", то выражение find принимает следующий вид: искать файлы определенного типа "И" с определенным именем "И" более новые, чем мой файл .cshrc.

Продемонстрируем разницу между логическим "И" и логическим "ИЛИ". Если я хочу найти все файлы в моем домашнем каталоге, которые не использовались последние 7 дней "И" их размер больше чем 10Мб, то я использую следующую команду:
find . -atime +7 -size +20480 -print

Однако, если я хочу найти файлы, которые не использовались последнюю неделю "ИЛИ" их размер больше чем 10Мб, то мне придется выполнить такую команду:
find . -atime +7 -o -size +20480 -print

Если вы будете использовать ключ "-size", вам придется немного повычислять, так как он использует в качестве параметра число 512-байтных блоков в файле. Я могу воспользоваться командой expr для облегчения этого процесса:
find . -atime +7 -o -size +`expr 10 * 1024 * 2` -print

Заметьте, что в этой команде в обратных кавычках (это знак ` в левом верхнем углу вашей клавиатуры) заключено то, что надо вычислить. Нам остается поставить перед выражением знак плюс, так как мы хотим найти файлы "более чем" 10Мб размера.

Для того что бы поглядеть заранее то, что сосчитает команда expr, припишите впереди команды find команду echo:
echo find . -atime +7 -o -size +`expr 10 * 1024 * 2` -print
find . -atime +7 -size +20480 -print

Вообще проверка сложного выражения find, для того что бы увидеть его полный вид, перед выполнением, является хорошим тоном.

http://www.onlamp.com/pub/a/bsd/2002/02/21/FreeBSD_Basics.html.

Обновлено: 12.03.2015