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


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


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

Давайте продолжим с места, на котором мы остановились в прошлый раз:
find . -atime +7 -o -size +'expr 10 * 1024 * 2' -print

Как вы помните, эта команда ищет файлы в текущем каталоге и его подкаталогах (текущий каталог указывается первым параметром "."), к которым не обращались более семи дней ("-atime +7") или (на операцию логического "ИЛИ" указывает параметр "-o") размер которых превышает определенное значение (ключ "-size +"). Для подсчета размера используется команда expr. Поскольку мне хотелось найти файлы более 10Мб, а find оперирует размерами указанными в 512-ти байтных блоках, то мне нужно умножить 10 на 1024 и на 2 (т.к. 512*2 = 1024). (Не стоит забывать о возможности указания размера искомых файлов в байтах - для этого после числа надо, без пробела, поставить символ "с" - прим. переводчика).

Заметьте, что я использовала символ обратного апострофа "'" (он находится в левом верхнем углу клавиатуры). Когда вам требуется в системе UNIX подставить в параметры одной команды в результат работы другой, следует поместить команду, результат которой вам необходим, в обратные апострофы. Это так называемая подстановка команд. Помещая команду, производящую вычисления между обратными апострофами, я тем самым подставляю результат вычисления в параметр ключа "-size" команды find.

Последнее, на что я хочу обратить ваше внимание, это то что я поставила перед символами "*" обратные слеши "". Дело в том, что в вычислениях "звездочка" означает умножение, тогда как интерпретатор команд считает его подстановочным символом. Однако если перед "звездочкой" поставить знак обратного слеша, интерпретатор команд не будет считать его символом подстановки, в то время как команда expr воспримет пару символов "*" как знак умножения.

Наконец перейдем к следующим примерам. Давайте будем считать, что я имею некий каталог, содержащий большую ветвь файловой системы, в котором я хочу найти все файлы, удовлетворяющие определенным условиям, после чего эти файлы следует удалить. Существует несколько способов сделать это при помощи команды find. Давайте попробуем их сравнить.

В моем домашнем каталоге есть подкаталог tmp, в котором, в свою очередь, находится подкаталог tst. В каталоге tst содержится много файлов и каталогов, некоторые из которых имеют расширение .old. Давайте подсчитаем, сколько файлов находится в каталоге tst:
cd ~/tmp/tst
find . -print | wc -l
269

Обратите внимание, что команда find печатает каждый найденный файл в одной строке. Используя канал я передам результат работы find команде wc (word count - подсчет слов), заставив ее при помощи ключа -l считать количество строк. В результате выполнения этой операции я узнала, что в каталоге ~/tmp/tst 269 файлов (включая каталоги, поскольку в UNIX каталог на самом деле является особым видом файла). Посмотрим, сколько из них имеет расширение .old:
find . -name "*.old" -print | wc -l
67

Как мне теперь удалить эти файлы? Один из способов - использование ключа -exec, с командой rm в качестве параметра, например так:
find . -name "*.old" -exec rm {} ;

Когда команда выполнится, я могу повторить поиск для проверки наличия файлов с расширением .old:
find . -name "*.old" -print | wc -l
0

Теперь мы убедились, что файлы удалены. Однако, для удаления большого количества файлов, такой способ не является лучшим. Дело в том, что когда вы используете ключ -exec, для каждого найденного файла создается отдельный процесс для выполнения команды - параметра ключа -exec. Это скорее всего не создаст проблем на вашем домашнем компьютере, где вы собираетесь найти всего несколько файлов, однако если вы попытаетесь таким способом найти и обработать сотни или тысячи файлов на рабочем сервере, то проблемы появятся - дело в том, что, к сожалению, этот метод требует наибольшее количество ресурсов и работает медленнее, чем все остальные.

Воспользуемся для удаления файлов другим способом:
find . -name "*.old" -print | xargs rm

Заметьте, что в данном случае не используются завершающие символы ";", так как они необходимы только при использовании ключа "-exec". Используя команду xargs в этом примере, я опять стираю все файлы, оканчивающиеся на .old, однако в отличие от предыдущего способа, при этом создается только один процесс. Команда find создает список найденных и подлежащих удалению файлов. Этот список передается команде xargs, которая преобразует строки списка в одну большую строку, разделенную пробелами, затем эта строка передается команде rm.

И наконец воспользуемся третьим способом удаления файлов, используя ключ "-delete" команды find:
find . -name "*.old" -delete

Эта команда имеет самый простой синтаксис из всех трех и, к тому же, является самым эффективным способом удаления файлов. Ключ "-delete" вообще не требует создания отдельного процесса, так как все операции по удалению файлов производятся процессом find. К тому же, этот способ будет работать в любом случае, в отличие от второго способа использованием команды xargs, которая может привести к ошибке, если будет найдено слишком много файлов и они не смогут убраться в буфер командной строки. Если вы будете что-нибудь искать в многоуровневой системе каталогов или имена найденных файлов будут слишком длинные, то, скорее всего, вы натолкнетесь на это ограничение. Если вас интересует размер этого буфера, воспользуйтесь следующей командой:
sysctl -a | grep kern.argmax
kern.argmax: 65536

Число 65536 означает максимальное количество байт (символов) в буфере аргумента.

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

Она выведет вам список файлов, подходящих под образец. Если список файлов соответствует вашим ожиданиям, воспользуйтесь ключом "-delete", как описано выше. Однако есть и другой способ - вы можете воспользоваться ключом "-ok":
find . -name "*.old" -ok rm ;

Ключ "-ok" заставит команду find выдавать запрос перед удалением файла. Обратите внимание, что для удаления я использовала команду rm, а не ключ "-delete". Символы окончания команды ";" требуются как и в случае ключа "-exec".

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

При помощи ключа "-ls" можно получить следующую информацию о найденных файлах: номер inode в файловой системе, размер в блоках, права доступа, количество жестких ссылок (hard link), имя владельца, группу, размер в байтах, время последней модификации, а также имя найденного файла. Следующая команда показывает мне первые десять подкаталогов в моем домашнем каталоге. Обратите внимание на то, что для поиска каталогов используется ключ "-type" с параметром "d":
cd
find . -type d -ls | head
976142 8 drwxr-xr-x 39 genisis wheel 4096 Mar 3 17:52 .
1413099 2 drwxr-xr-x 2 genisis wheel 512 Mar 3 13:38 ./pdfs
373539 2 drwxr-xr-x 2 genisis wheel 512 Feb 6 12:38 ./tst
1087249 2 drwxr-xr-x 2 genisis wheel 512 Oct 4 07:29 ./perlscripts
650764 2 drwx------ 2 genisis wheel 512 Mar 3 17:52 ./mail
706616 2 drwx------ 4 genisis wheel 512 Sep 22 14:29 ./.kde
706635 2 drwx------ 11 genisis wheel 512 Nov 7 12:36 ./.kde/share
706636 4 drwx------ 3 genisis wheel 1536 Mar 2 18:38 ./.kde/share/config
785986 2 drwx------ 2 genisis wheel 512 Sep 22 14:36 ./.kde/share/config/colors
706682 2 drwx------ 3 genisis wheel 512 Mar 2 18:36 ./.kde/share/fonts


Давайте попробуем еще чему-нибудь научиться, используя ключ "-ls". Ранее, для того чтобы узнать, сколько файлов подходит под определенный поисковый запрос, мы использовали перенаправление вывода в команду wc -l. Воспользуемся этим для подсчета количества подкаталогов в моем домашнем каталоге:
find . -type d -print | wc -l
256

На самом деле он содержит только 255 подкаталогов, так как первый результат поиска - сам родительский каталог. Теперь попробуем побольше выяснить о структуре этих каталогов, используя следующую конструкцию:
find . -type d -ls | awk '{print $4 - 2, $NF}' | sort -rn | head
37 .
26 ./.kde/share/apps/kio_http/cache
18 ./.kde/share/apps
15 ./.gimp-1.2
9 ./tmp/tst
9 ./.kde/share
8 ./tmp/tst/h
8 ./tmp/tst/g
8 ./tmp/tst/f
8 ./tmp/tst/e

Великолепно! Мы видим, что в моем домашнем каталоге "." - 37 подкаталогов, а в каталоге "./.kde/share/apps/kio_http/cache" - 26 и так далее. Теперь разберемся, как работает эта команда. Для начала я воспользовалась ключом "-ls", который дает нам полную информацию о каждом найденном каталоге. Эта информация передается утилите awk, которая используется для извлечения данных из определенных полей. Вспомните, что результатом работы ключа "-ls" является список, состоящий из следующих полей: номер inode, количество блоков, права, количество ссылок и т.п. Я запрограммировала awk так, чтобы он брал информацию из четвертого столбца (который содержит количество ссылок и в awk обозначается $4) и вычел из него двойку (так как меня не интересуют каталоги "." и ".."). Еще мне необходимо название каждого каталога и поскольку оно содержится в самом последнем столбце, я воспользовалась переменной $NF для получения этой информации. Поместив эти инструкции в фигурные скобки "{}", я запрограммировала awk на выполнение этих действий с каждой строкой данных, поступающих от команды find. В свою очередь, результат выполнения команды awk передается в команду sort. Используя ключи "-rn", я задаю обратный порядок сортировки (от большего к меньшему), для того чтобы увидеть, какие каталоги содержат больше подкаталогов. Так как я не хочу смотреть на весь получившийся результат, я пользуюсь командой head, передавая ей результат работы команды sort, в результате чего получаю лишь первые десять строк этого результата.

Следующий ключ, на котором мы остановимся - "-perm". Если мы хотим найти все файлы, которые имеют права доступа "777", что означает доступ на чтение, запись и выполнение для всех, то нам следует воспользоваться командой:
find . -perm 777 -print

Вышеуказанная команда ищет файлы с правами "777". Если вы озабочены поиском файла, в правах которого установлен конкретный бит (независимо от остальных), то воспользуйтесь чем-нибудь подобным (обратите на знак "-" перед числом задающим права доступа - прим. переводчика):
find . -perm -4000 -print

Этот пример выдаст вам только те файлы, у которых установлен SUID-бит. Другая полезная команда выглядит так:
find . -perm -0002 -print

Она найдет вам файлы, которые доступны "на запись" для "всех". Заметьте, что -0002, -002, -02 и -2 дадут вам одинаковый результат, так как нули в начале подразумеваются.

Последние два ключа, которые я опишу в этой статье, полезны при резервном копировании или репликации каталогов. Начнем с ключа "-depth". Скажем, я хочу провести резервное копирование моего домашнего каталога в смонтированный каталог /backup (имеется в виду копирование со всеми правами и т.п. - прим. переводчика). Я могу это сделать следующей командой:
find . -depth -print | cpio -dump /backup

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

Однако, если вы вспомните о существовании ключа "-depth", то команда find будет формировать список найденных файлов, начиная с нижних уровней вложенности. Это означает, что содержимое каталога будет в списке раньше, чем его название. А это в свою очередь означает, что файлы, находящиеся в исходном каталоге, будут помещены cpio в целевой каталог раньше, чем она наложит на него права.

А как поступить, если мне не хочется реплицировать всю структуру домашнего каталога, а только его часть? Здесь может помочь ключ "-prune". Давайте представим, что я хочу скопировать весь мой домашний каталог, за исключением каталога tmp. Я могу поступить следующим образом:
find . -type d -name tmp -prune -o -print | cpio -dump /backup

Синтаксис команды выглядит немного знакомо: я использую ключ "-name" для нахождения всех каталогов ("-type d") с названием tmp и все такие каталоги пропускаю "-prune". Затем идет логическое "ИЛИ" ("-o"), в результате которого все остальные найденные файлы печатаются ("-print") и передаются команде cpio.

Надеюсь, что примеры приведенные в этих двух статьях, посвященных поиску при помощи команды find, помогут вам разобраться с ее синтаксисом и возможностями.

http://www.onlamp.com/pub/a/bsd/2002/03/14/FreeBSD_Basics.html.

Обновлено: 12.03.2015