Поиск файлов командой find
find обходит дерево каталогов и ищет файлы по имени, типу, размеру или времени изменения. -exec запускает команду для каждого результата; -print0 с xargs -0 безопасно обрабатывает имена файлов с пробелами.
Глобы ищут только в текущем каталоге. Когда нужно охотиться по всему дереву каталогов — найти все .log-файлы под /var, все файлы больше 100 МБ или все файлы, изменённые за последние 24 часа — нужен find. Он рекурсивно обходит дерево, фильтрует по почти любому атрибуту файла и может выполнять команду для каждого совпадения.
К концу урока ты будешь писать команды find с фильтрацией по имени, типу, размеру и времени изменения, а также безопасно выполнять действия над результатами.
После этого урока ты сможешь использовать find для поиска в дереве каталогов по имени (-name), типу (-type), размеру (-size) и времени изменения (-mtime); выполнять команду для каждого результата с -exec; и безопасно обрабатывать имена файлов с пробелами через -print0 и xargs -0.
Базовый синтаксис: find <путь> <выражение>. Первый аргумент — корень поиска; всё остальное — предикаты. Без предикатов find выводит каждый найденный файл:
find /var/log # список всех файлов под /var/log рекурсивно
find . # список всех файлов в текущем каталогеПредикаты вычисляются слева направо и объединяются неявным И. Добавь -maxdepth N, чтобы ограничить глубину рекурсии.
-name и -iname фильтруют по имени файла. Шаблон использует glob оболочки, но должен быть взят в кавычки, чтобы оболочка не раскрыла его до того, как find его увидит:
find . -name "*.log" # файлы, оканчивающиеся на .log
find /etc -name "nginx.conf" # поиск точного имени
find . -iname "readme*" # без учёта регистра; находит README.md, readme.txt-name сопоставляется только с именем файла, не с полным путём. Для сопоставления с путём используй -path "*/dir/*.log".
-type фильтрует по типу записи. Распространённые значения:
| Флаг | Значение |
|---|---|
-type f | обычный файл |
-type d | каталог |
-type l | символическая ссылка |
find . -type f -name "*.sh" # только файлы, не каталоги с именем *.sh
find /tmp -type d # список всех каталогов под /tmpСочетание -type f с -name — наиболее распространённый шаблон.
-size фильтрует по размеру файла. Суффиксы: c (байты), k (кибибайты), M (мебибайты), G (гибибайты). Используй + для «больше» и - для «меньше»:
find /var/log -size +10M # файлы больше 10 МиБ
find . -size -1k # файлы меньше 1 КиБ
find /home -type f -size +100M # большие файлы, занимающие место-mtime N фильтрует по времени изменения в днях. +N означает «старше N дней»; -N означает «изменён в течение последних N дней»:
find . -mtime -1 # изменены за последние 24 часа
find . -mtime +30 # не изменялись более 30 дней
find . -mmin -60 # изменены в течение последних 60 минут (-mmin в минутах)-exec запускает команду для каждого совпадения. Заполнитель {} заменяется именем найденного файла. Выражение должно заканчиваться на \; (один вызов на файл) или + (все файлы в одном вызове):
# Удалить каждый .tmp файл (один вызов rm на файл)
find . -name "*.tmp" -exec rm {} \;
# Удалить каждый .tmp файл (один вызов rm со всеми файлами — быстрее)
find . -name "*.tmp" -exec rm {} +
# Вывести размер каждого файла больше 10M
find /var -size +10M -exec du -sh {} \;-print0 с xargs -0 — самая безопасная альтернатива, когда имена файлов могут содержать пробелы или переносы строк:
find . -name "*.log" -print0 | xargs -0 gzip-print0 разделяет результаты нулевым байтом вместо переноса строки; xargs -0 читает разделённый нулями ввод. Это выживает при именах вроде мой файл.log.
Поиск и сжатие старых логов.
На сервере под /var/log/app накапливаются логи. Нужно сжать каждый .log-файл, не изменявшийся 7 и более дней:
find /var/log/app -type f -name "*.log" -mtime +7 -print0 \
| xargs -0 gzip --bestРазбор:
-type f— только обычные файлы, пропуская каталоги-name "*.log"— только лог-файлы (шаблон в кавычках, чтобы оболочка не раскрыла его)-mtime +7— старше 7 дней-print0 | xargs -0 gzip --best— передаёт все совпадения в gzip одной пакетной операцией, с нулевым разделителем для безопасности
# Проверка: список всех сжатых файлов, созданных сегодня
find /var/log/app -name "*.gz" -mtime -1▸Почему это работает
BSD find на macOS отличается от GNU find на Linux. Ключевые отличия:
-mtime: GNU округляет до ближайшей 24-часовой границы; BSD строго 24 часа × N.-exec {} +(пакетный режим) — расширение GNU; BSDfindна macOS его не поддерживает. Для кроссплатформенных скриптов используй-print0 | xargs -0.- GNU
findподдерживает-printfдля форматированного вывода; BSD — нет. Используйstatкак обходной путь на macOS.
Если пишешь скрипты для обеих платформ, тестируй на macOS явно или используй gfind (GNU findutils через Homebrew).
▸Частая ошибка
Забыть взять шаблон -name в кавычки. Если в текущем каталоге есть файл, совпадающий с глобом, оболочка раскроет его до того, как find увидит аргумент -name:
# Представь, что в текущем каталоге есть error.log
find /var/log -name *.log # оболочка раскрывает в: find /var/log -name error.logТеперь find ищет только файл с именем error.log, а не все .log-файлы. Этот баг непостоянен и сложно заметить. Всегда бери в кавычки аргументы -name и -path: find /var/log -name "*.log".
Какая команда find удаляет все .tmp-файлы под /tmp, безопасно обрабатывая имена файлов с пробелами?
find <путь> <предикаты> обходит дерево каталогов и выводит (или обрабатывает) каждую запись, соответствующую всем предикатам. Ключевые предикаты: -name (glob имени файла — всегда брать в кавычки), -type f/d/l (тип записи), -size +N/-N (размер файла), -mtime +N/-N (время изменения в днях). -exec команда {} \; запускает команду для каждого файла; -exec команда {} + объединяет все совпадения в один вызов (быстрее, но недоступно в BSD find). -print0 | xargs -0 — самый безопасный конвейер при именах файлов с пробелами.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.