Friday, December 6, 2019

Команда find: основные параметры и примеры.

Оригинал: https://www.booleanworld.com/guide-linux-find-command/

Find - часть пакета findutils. Она позволяет искать файлы и каталоги, задавая различные условия, а также выполнять разные действия над результатом поиска.

ВВЕДЕНИЕ.

Основная структура команды выглядит так:

    find [paths] [expression] [actions]

Команда может принимать несколько путей поиска и искать в них рекурсивно. То есть, если в заданном каталоге поиска встречается подкаталог, find переходит в него и ищет там, а также переходит в каталоги внутри этого подкаталога.

По умолчанию find находит всё, что есть в каталоге, но можно задать фильтры. Действие по умолчанию - вывод результатов на stdout, то есть print, но можно задавать и другие команды.

После рассмотрения примеров станет понятнее, о чем речь.

ПОИСК ФАЙЛОВ И КАТАЛОГОВ.

Допустим, вам нужен список всех файлов и подкаталогов какого-то каталога. Например, /usr/share.

    find /usr/share

Команда выдаст список вида:


/usr/share
/usr/share/dict
/usr/share/gnome-doc-utils
/usr/share/gnome-doc-utils/templates
/usr/share/gnome-doc-utils/templates/gnome-app-template.xml
/usr/share/gnome-doc-utils/templates/legal.xml
.......
который может быть очень длинным.

Если вам нужно вывести содержимое нескольких каталогов, их нужно указать:

    find /sbin /bin /usr/sbin

Без аргументов будет выведено содержимое текущего каталога - .


ПОИСК ПО НАЗВАНИЮ.

В начале мы говорили о возможности фильтровать вывод. Обсудим это подробнее.

Поиск по шаблону NEWS.txt в каталоге /usr:

    find /usr -name NEWS.txt

Ключ -name предполагает регистрозависимый поиск. Если не нужно учитывать регистр, вместо name используется iname.

Ключи -iname и -name принимают wildcards - специальные символы, заменяющие другие символы. Этих символов два:" ? "- любой одиночный символ;" * " -  любое количество любых символов, включая 0 - то есть их отсутствие. При использовании спецсимволов, параметр для name\iname нужно заключать в одинарные или двойные кавычки, чтобы оболочка командной строки не раскрыла их как свои регулярные выражения.

Поиск файлов с расширением .txt в каталоге /usr:

    find /usr -name '*.txt'

Поиск файлов и подкаталогов, название которых состоит из 4 букв:

    find /usr -name '????'

Для поиска не по имени, а по полному пути используется ключ -path. Например, вам нужно найти все файлы и каталоги с окончанием ".txt" в подкаталоге src:

    find /usr -path '*/src/*.txt'

Для игнорирования регистра используется ключ -ipath в подкаталоге src:

    find /usr -path '*/src/*.txt'

Для игнорирования регистра используется ключ -ipath.


ПОИСК ФАЙЛОВ ИЛИ КАТАЛОГОВ.

До сих пор команда выводила и файлы, и каталоги. Если вам нужны или файлы, или каталоги, воспользуйтесь ключом -type. Наиболее используемые параметры для type:

 f - files; d - directories, l - symbolic links.

То есть поиск всех файлов в каталоге будет выглядеть так:

    find /usr -type f -name '*.txt'

Поиск подкаталогов, начинающихся с 'python':

    find /usr -type d -name 'python*'

ПОИСК ПУСТЫХ ФАЙЛОВ И КАТАЛОГОВ.

Для этого есть флаг -empty. Пустой файл - это файл без содержимого, а пустой каталог - это каталог, в котором нет файлов или подкаталогов.
Например, поиск пустых каталогов в домашней директории:

    find ~ -type d -empty

ИНВЕРСИЯ ВЫБОРА.

Это полезно, когда нужно исключить из поиска соответствующие критерию сущности. Например, найти все файлы в каталоге, название которых не заканчивается на '.txt' :

    find /usr -type f ! -name '*.txt'

ПОИСК ПО ВЛАДЕЛЬЦУ.

Для поиска файлов и каталогов, принадлежащих определенному пользователю, есть ключ -user. Например, найти все файлы  в системе, принадлежащие пользователю booleanworld:

    find / -type f -user booleanworld

Можно также передавать не имя, а ID пользователя, который выясняется командой id:

    id -u <username>

Аналогично, есть ключ -group для поиска файлов и каталогов, принадлежащих группе. ID группы выясняется командой id:

    id -g <username>

ПОИСК ПО ДАТЕ И ВРЕМЕНИ.
В Линуксе есть 3 типа временных меток, связанных с файлом:

 ▪ Modification time (mtime): время последнего изменения содержимого.
 ▪ Access time (atime): время, когда файл открывали на чтение.
 ▪ Change time (ctime): время последней модификации содержимого файла или его атрибутов.

Ключи команды find для поиска по времени, соответственно: -mtime, -atime, -ctime. Они также позволяют фильтровать файлы по количеству дней. День - это период в 24  часа.

Например:

-mtime 2: поиск файла, измененного 2 дня назад, т.е. от 48 до 72 часов назад.
-mtime -2: файл изменен менее 48 часов назад, т.е. в последние 48 часов.
-mtime +2: файл изменен более 2 дней назад - как минимум 3 дня назад, т.е. 72 и более часа назад.

Ключи atime и ctime работают так же.

Если день - это слишком долго, есть ключи по минутам: -mmin, -amin, -cmin. Так, -amin +5 - это файлы, доступ к которым в последний раз был более 5 минут назад. Флаги можно комбинировать. Например, файлы, которые были изменены 2 дня назад, а последний доступ был 5 минут назад:

find /usr -type f -mtime 2 -amin 5

Можно использовать один флаг несколько раз. Например, файлы, измененные от 50 до 100 дней назад:

find /usr -type f -mtime +50 -mtime -100 # Не факт, что у вас сработает.

ПОИСК ПО РАЗМЕРУ.

Ключ -size. Каталоги не будут отображаться, так как не имеют размера. Размер указывается числом, за которым следует буква, обозначающая единицы измерения:

 c - байт; k - килобайт; M -мегабайт; G - гигабайт.

Так же, как со временем, + и - обозначают "более чем" и "менее чем".

Например, файлы больше 1 Гб:

    find / -size +1G

ПОИСК ПО ПРАВАМ ДОСТУПА.

Ключ -perm позволяет искать по правам доступа как в числовом, так и в символьном виде.

▪ СИМВОЛЬНЫЕ ПРАВА.
Допустим, вы хотите найти все файлы и подкаталоги в /usr с правами rwxr-xr-x:

    find /usr -perm u=rwx,g=rx,o=rx

где u - это пользователь-владелец файла; g - пользователи, входящие в группу-владельца; о - other, т.е. пользователи, не входящие в группу, владеющую файлом.

Буква a обозначает all - всех пользователей.

Например, найти все файлы и каталоги в системе с правами  r-xr-xr-x:

    find /usr -perm a=rx

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

    find / -type f -perm /a=x

▪ ЧИСЛЕННЫЕ ПРАВА.
Первый пример в предыдущей части - файлы с правами rwxr-xr-x в каталоге /usr:

    find /usr -perm 644

Сложнее найти подмножество прав. Как мы упомянули ранее, поиск файлов, которые могут исполнять все пользователи, включает в себя проверку, установлен ли бит исполнения в 1. Другие биты нас не интересуют. Бит, который нам нужно проверить, мы берем равным 1, а остальные - равными 0. Таким образом мы получим двоичное число, которое переведем в восьмеричное:

(001  001  001)₂ = 111₈
user group other

Затем перед восьмеричным числом надо поставить "-" как индикатор поиска по подмножеству:

    find / -type f -perm -111

!!! Примечание переводчика: не знаю, может у автора другая версия find, чем у меня, но вообще, "/" обозначает, что бит выставлен у какого-либо типа пользователей (u,g, или o, или у всех), то есть как нестрогая дизъюнкция; "-" означает, что все заявленные биты выставлены, то есть как конъюнкция. Это описано в мануале. Т.е. /a=x значит, что бит исполнения вообще есть в правах - например, rwx--, - а -111 (или -а=х) значит, что бит выставлен у всех пользователей.

ПОИСК ПО ФЛАГАМ В ПРАВАХ ДОСТУПА.

Флаг suid  в символьном виде выражается как 4 перед основными правами. Таким образом, чтобы найти файлы с суидным битом, нужно ввести:

find / -type f -perm -4000(/4000)

* Прим. переводчика: В этом случае "-" и "/" эквивалентны, но если нужно задать ещё другие права, то вывод команд поиска будет разным.
Аналогично, set group id обозначается цифрой 2 перед правами, а  sticky bit  - цифрой 1. Если биты сочетаются, то их значения складываются. То есть, если нужно найти файлы со битами suid и sgid, то шаблон будет 6000.

В символьном режиме шаблон поиска будет выглядеть примерно так:

    find / -type f -perm /u=s

Шаблон для бита sgid - /g=s; для sticky - /o=t.

ОГРАНИЧЕНИЕ ГЛУБИНЫ РЕКУРСИВНОГО ПОИСКА.

Ключ -maxdepth n, где n - количество уровней, обозначает максимальную глубину. Ключ -mindepth n - не проводить поиск на уровнях выше, чем n.

ЛОГИЧЕСКИЕ ОПЕРАТОРЫ В КОМАНДЕ FIND.

Команда поддерживает ключи -a (and) и -o (or), а также группировку частей выражения с помощью скобок. Скобки надо экранировать бэкслешем или брать в кавычки, чтобы шелл не принимал их за свое регулярное выражение. При указывании нескольких флагов одного за другим find считает, что вы используете and (expr1 expr2).

Например, рассмотренный ранее поиск каталогов, названия которых начинаются с 'python':

find /usr -type d -name 'python*'

Оператор and можно указать явно: 

find /usr -type d -a -name 'python*'

Рассмотрим другой пример: нужно найти файлы/каталоги в системе, которые были изменены в последние 5 минут или более 50 дней назад:

find / -mmin -5 -o -mtime +50

Теперь введем дополнительное ограничение к предыдущей задаче: нужно найти только файлы. Так как приоритет and выше, чем or, то понадобятся скобки:

find / '(' -mmin -5 -o -mtime +50 ')' -a -type f
Так как -а в данном случае подразумевается, его можно опустить.

ДЕЙСТВИЯ НАД РЕЗУЛЬТАТОМ ПОИСКА.

Команда поддерживает различные действия, такие как копирование или удаление. Действие по умолчанию - вывод результатов на stdout.

▪ Удаление.
Действие -delete. Работает как с файлами,так и с каталогами. Например, чтобы удалить все пустые директории в домашнем каталоге:

find ~ -type d -empty -delete

▪ Выполнение произвольной команды.
Можно выполнить свою команду, использовав ключ -exec. Предположим, вы хотите сделать резервную копию mp3 файлов из вашего домашнего каталога на внешний диск. Пусть этот диск смонтирован в /media/MyDrive. Чтобы скопировать 1 файл, нужно дать следующую команду: cp <path to file> /media/MyDrive

Чтобы скопировать все файлы:

find ~ -type f -name '*.mp3' -exec cp {} ';'

где "{}" заменяет путь к файлу; ";" - индикатор окончания ввода аргументов. Оба символа нуждаются в экранировании, так как являются спецсимволами также для оболочки командной строки.
Каждый раз, когда find находит файл, подходящий под условие поиска, она заменяет фигурные скобки путём к файлу и выполняет команду для этого файла.
Ещё один важный пример - поиск строки во множестве файлов. Чтобы найти строку "hello" в файле, используется grep:
grep hello <filename>
Если вы знакомы с grep, у вас может возникнуть соблазн дать следующую команду:
find ~ -type f -exec grep hello {} ';'
Однако, если вы это сделаете, то сразу осознаете ошибку: нам нужен список файлов, в которых содержится строка "hello", а мы получим список строк, содержащих шаблон. Ключ -l команды grep используется для вывода путей к файлам, содержащим шаблон.
find ~ -type f -exec grep -l hello {} ';'

ЕЩЁ ОДИН ВАРИАНТ ДЕЙСТВИЙ НАД РЕЗУЛЬТАТОМ.

Допустим, вам нужно создать сжатый архив. Например, вы хотите заархивировать и сжать все mp3-файлы в своем домашнем каталоге. Вероятно, вы сделаете так:
find ~ -type f -name '*.mp3' -exec tar -czf music.tar.gz {} ';'
Однако, если вы посмотрите содержание архива, то увидите, что там только один файл mp3. Запомните, что find выполняет команду каждый раз, когда находит новое совпадение. Итак, предыдущий файл каждый раз переписывался новым архивом.

Эта проблема решилась бы, если бы можно было указать команде find передавать tar список файлов после того, как найдены все совпадения. В этом случае нам нужен второй вариант ключа -exec, когда вместо ";" в конце команды ставится "+":

find ~ -type f -name '*.mp3' -exec tar -czf music.tar.gz {} +

ВЫПОЛНЕНИЕ ДЕЙСТВИЙ НАД КАТАЛОГАМИ.

Иногда нужно выполнить команду, аргументом которой должен быть каталог, в котором находится искомый файл/каталог. Для этого есть ключ -execdir. Он аналогичен ключу -exec во всем, и даже имеет варианты с ";" и "+".
* У меня в мануале ключ execdir описывается как более безопасный вариант exec  - в случае execdir команда выполняется из подкаталога, содержащего искомый файл, который обычно отличается от каталога, из которого вы вызываете find.

ПРОСМОТР ИНФОРМАЦИИ О ФАЙЛЕ.

Если вы хотите увидеть метаданные файла/каталога, такие как права доступа и размер, нужно использовать ключ -ls. Например, вам нужны детали о файлах в системе, размер которых больше 1 Гб:

find / -type f -size +1G -ls

СКРЫТИЕ СООБЩЕНИЙ ОБ ОШИБКАХ.

Иногда во время выполнения команды find вы можете получить сообщения типа "Permission denied". Эти сообщения можно скрыть, перенаправив их в /dev/null:

find [paths] [expression] [actions] 2>/dev/null