Команда find в Linux: опции -name, -type, -size, -mtime и -delete
Команда find в Linux спускается по порядку от начальной папки и находит файлы и папки, подходящие под условия: имя, тип, размер, время изменения и права. Когда мы создали десять файлов в тестовой папке на сервере AlmaLinux 9 и запускали команду, меняя по одному условию, найденные файлы стали передавать через -exec другим командам, а при удалении сначала проверять список с теми же условиями и только потом применять -delete.
Пути тестовой папки и время файлов в блоках результатов заменены примерными значениями. В выводе сервера Debian 12 примерное значение только у пути домашней папки, остальное приведено без изменений.
1️⃣ Как использовать команду find в Linux
Базовая форма: [find начальный-путь условия]. Без начального пути поиск идёт в текущей папке (.), а без условий выводятся все файлы и папки под начальным путём.
Тест проводился в /root/work с тремя папками и десятью файлами, как показано ниже.
find /root/work/root/work
/root/work/docs
/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/backup
/root/work/report.txt
/root/work/README.md
/root/work/run.sh
/root/work/my notes.txt
/root/work/empty.txtfind не сортирует результаты по имени; в этом запуске, дойдя до папки docs, он сначала вывел все файлы внутри, а затем перешёл к logs.
Если записать несколько условий подряд, останутся только файлы, подходящие под все.
-name сравнивает с шаблоном только имя файла, а не путь. Заключайте шаблон в одинарные кавычки, чтобы оболочка не раскрыла * первой и передала его в find как есть.
find /root/work -name '*.txt'/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/report.txt
/root/work/my notes.txt
/root/work/empty.txt-name учитывает регистр. Поиск имён, начинающихся с readme, нашёл только docs/readme.txt, а с -iname нашёлся и README.md.
find /root/work -name 'readme*'/root/work/docs/readme.txtfind /root/work -iname 'readme*'/root/work/docs/readme.txt
/root/work/README.mdfind входит в пакет findutils. Версия в первой строке [find --version]: findutils 4.8 на сервере AlmaLinux 9.8 и 4.9 на сервере Debian 12.
2️⃣ find -type d и -maxdepth
-type f оставляет только обычные файлы, а -type d только папки.
find /root/work -type d/root/work
/root/work/docs
/root/work/logs
/root/work/backupНачальный путь /root/work тоже папка, поэтому он появился в первой строке. С -type f на том же пути появились все десять файлов.
-maxdepth задаёт, на сколько уровней спускаться от начального пути. С -maxdepth 1 файлы в docs и logs выпали, и остались только пять файлов прямо под /root/work.
find /root/work -maxdepth 1 -type f/root/work/report.txt
/root/work/README.md
/root/work/run.sh
/root/work/my notes.txt
/root/work/empty.txtНаоборот, -mindepth 2 пропустил всё прямо под начальным путём и вывел только пять файлов во вложенных папках.
find /root/work -mindepth 2 -type f/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmpСтраница man советует писать глобальные опции вроде -maxdepth сразу после начального пути. -maxdepth 1 после -type f дал те же пять файлов, но в терминале появляется строка предупреждения с просьбой ставить глобальные опции вперёд (по странице man).
Чтобы узнать только количество папок или файлов, передайте вывод в wc -l. Подсчёт вывода -type d дал 4 с учётом начального пути.
find /root/work -type d | wc -l4С -maxdepth 1 тоже получилось 4, потому что все три вложенные папки лежат прямо под начальным путём.
find /root/work -maxdepth 1 -type d | wc -l4Подсчёт только файлов во вложенных папках через -mindepth 2 дал 5.
find /root/work -mindepth 2 -type f | wc -l53️⃣ Условия размера и времени команды find
+ перед числом означает больше этого значения, - меньше, а без знака ровно это значение. Единицы -size указаны в таблице ниже (по странице man).
| Единица | Размер | Да |
|---|---|---|
| c | 1 байт | -size +500c |
| k | 1,024 байта | -size +1k |
| M | 1,048,576 байт | -size +1M |
| G | 1,073,741,824 байта | -size +1G |
| (нет) | Блоки по 512 байт | -size +2 |
find /root/work -type f -size +1k/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txtБольше 1k оказались guide.txt на 5,000 байт, app.log на 2MB и report.txt на 1,200 байт.
find /root/work -size +1M/root/work/logs/app.logКогда -size записали дважды как диапазон, остались только два файла больше 1k и меньше 100k.
find /root/work -type f -size +1k -size -100k/root/work/docs/guide.txt
/root/work/report.txt-size делит размер на единицу и округляет вверх перед сравнением. Поэтому README.md на 300 байт тоже считается за 1k, и при -size -1k появился только empty.txt размером 0.
find /root/work -type f -size -1k/root/work/empty.txtЧтобы проверить условия времени, мы задали время изменения README.md, my notes.txt и docs/readme.txt на 3 дня назад, а двух файлов .tmp на 10 дней назад. У каждого файла есть три времени: доступа, изменения и смены статуса, их можно посмотреть через команду stat.
-mtime считает время с последнего изменения в единицах по 24 часа. При -mtime -1 появились пять файлов, созданных за 24 часа.
find /root/work -type f -mtime -1/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txtПри -mtime +7 появились только два файла .tmp с датой 10 дней назад. Эта форма нужна, чтобы проверить список перед удалением старых журналов или временных файлов.
find /root/work -type f -mtime +7/root/work/docs/old.tmp
/root/work/logs/cache.tmp-mmin считает так же, но в минутах. Результат -mmin -60 совпал с -mtime -1: те же пять файлов.
find /root/work -type f -mmin -60/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-newer находит файлы, изменённые позже опорного файла. С README.md, заданным на 3 дня назад, в роли опорного появились пять файлов, созданных после него.
find /root/work -type f -newer /root/work/README.md/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-empty находит и файлы размером 0, и пустые папки: появились пустая папка backup и empty.txt.
find /root/work -empty/root/work/backup
/root/work/empty.txt4️⃣ Условия -perm и -user команды find
Если после -perm написать только число, ищутся файлы с правами ровно этого значения. -perm 644 исключил run.sh с 755 и my notes.txt с 600, и нашлось восемь файлов.
find /root/work -type f -perm 644/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/report.txt
/root/work/README.md
/root/work/empty.txtС - перед режимом ищутся файлы, у которых есть все указанные права. Право на выполнение для владельца (u+x) было только у run.sh.
find /root/work -type f -perm -u+x/root/work/run.shС / перед режимом достаточно любого из указанных прав. 6000 это сумма SUID (4000) и SGID (2000), и такая форма нужна для проверки на сервере файлов, которые при запуске работают с правами владельца или группы.
В тестовой папке таких файлов не было, поэтому мы дали run.sh SUID и запустили поиск: появился run.sh.
chmod u+s /root/work/run.shfind /root/work -type f -perm /6000/root/work/run.shВ старых материалах на этом месте стоит -perm +6000. Запуск в таком виде закончился ошибкой [invalid mode].
find /root/work -type f -perm +6000find: invalid mode '+6000'Форма + удалена в findutils 4.5.12 (по странице man), поэтому пишите /. Выданный для теста SUID убран через chmod u-s.
-user фильтрует по имени владельца. Все тестовые файлы принадлежат root, поэтому один -user root ничего не отсеивает; после добавления -name для выбора файлов .log остался только app.log.
find /root/work -type f -user root -name '*.log'/root/work/logs/app.log-nouser находит файлы, для UID владельца которых нет учётной записи. Это нужно для проверки файлов, оставшихся на сервере после удаления учётной записи; после смены владельца guide.txt на несуществующий UID 1500 появился guide.txt.
chown 1500:1500 /root/work/docs/guide.txtfind /root/work -nouser/root/work/docs/guide.txt-nogroup делает ту же проверку для группы, и появился guide.txt, у которого группа тоже изменена на 1500. После теста мы вернули всё через chown root:root.
С -o между условиями достаточно одного из них. При использовании с другим условием вроде -type f заключайте условия, связанные через -o, в скобки. Перед скобками ставьте обратную косую черту, чтобы оболочка не разобрала их первой, а между скобками и условиями оставляйте пробелы.
find /root/work -type f \( -name '*.log' -o -name '*.tmp' \)/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp! обращает условие сразу после него. Поиск файлов, не являющихся .txt, нашёл пять.
find /root/work -type f ! -name '*.txt'/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/README.md
/root/work/run.sh5️⃣ Использование find -exec
-exec запускает команду, указанную после него, для каждого найденного файла, а вместо {} подставляется найденный путь. ; в конце команды пишется как \;, чтобы оболочка не прочитала его первой как разделитель команд.
find /root/work -name '*.log' -exec ls -l {} \;-rw-r--r-- 1 root root 2097152 Oct 5 14:10 /root/work/logs/app.logЕсли закончить через + вместо \;, найденные пути собираются вместе, и команда запускается только один раз. При сравнении через echo \; вывел пять путей пятью строками, а + одной строкой.
find /root/work -name '*.txt' -exec echo {} \;/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/report.txt
/root/work/my notes.txt
/root/work/empty.txtfind /root/work -name '*.txt' -exec echo {} +/root/work/docs/readme.txt /root/work/docs/guide.txt /root/work/report.txt /root/work/my notes.txt /root/work/empty.txtКоманды, принимающие несколько путей, например ls, группируйте через +, чтобы сократить число запусков. Размер трёх файлов больше 1k мы проверили одним ls -lh.
find /root/work -type f -size +1k -exec ls -lh {} +-rw-r--r-- 1 root root 4.9K Oct 5 14:10 /root/work/docs/guide.txt
-rw-r--r-- 1 root root 2.0M Oct 5 14:10 /root/work/logs/app.log
-rw-r--r-- 1 root root 1.2K Oct 5 14:10 /root/work/report.txt-exec нужен и для массовой настройки прав веб-папки: 644 для файлов и 755 для папок. Перед запуском мы поискали файлы с другими значениями, и появились run.sh и my notes.txt.
find /root/work -type f ! -perm 644/root/work/run.sh
/root/work/my notes.txtУ файлов и папок разные права, поэтому запускайте отдельно с -type f и -type d. chmod тоже принимает несколько путей, поэтому мы закончили через +.
find /root/work -type f -exec chmod 644 {} +find /root/work -type d -exec chmod 755 {} +Повторный поиск с тем же условием ничего не дал, а папка backup, заранее изменённая на 700, тоже получила 755.
Однако run.sh тоже стал 644, и при запуске появилось [Permission denied]. В папках с исполняемыми файлами верните этим файлам право на выполнение через chmod +x.
Чтобы менять только файлы во вложенных папках, добавьте -mindepth 2. С 660 изменились только пять файлов в docs и logs, а после проверки мы вернули 644.
find /root/work -mindepth 2 -type f -exec chmod 660 {} +find /root/work -type f -perm 660/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmpСвязка find и grep ищет по содержимому файла, а не по имени. Чтобы смоделировать проверку веб-шеллов, мы создали четыре файла PHP в отдельной папке /root/site и добавили строку [base64_decode] в html-cache.php и photo.php в папке upload.
find /root/site -type f/root/site/lib/func.php
/root/site/upload/html-cache.php
/root/site/upload/photo.php
/root/site/index.phpЕсли имя веб-шелла известно, ищите через -name. С -maxdepth 2, чтобы спуститься только до папки загрузок, появился только html-cache.php.
find /root/site -maxdepth 2 -type f -name 'html*.php'/root/site/upload/html-cache.phpphoto.php с обычным именем здесь не появился. grep -l через -exec выводит только пути файлов со строкой, и появились оба файла.
find /root/site -name '*.php' -exec grep -l 'base64_decode' {} +/root/site/upload/html-cache.php
/root/site/upload/photo.php[base64_decode] это функция, которая есть и в файлах ядра WordPress, поэтому откройте найденные файлы и проверьте содержимое перед удалением.
-print0 ставит между путями нулевой символ вместо перевода строки. Нулевой символ не виден на экране, поэтому пути вышли слитно в одну строку.
find /root/work -name '*.txt' -print0/root/work/docs/readme.txt/root/work/docs/guide.txt/root/work/report.txt/root/work/my notes.txt/root/work/empty.txtЧтобы передать другой команде имена с пробелами вроде my notes.txt без разрыва, вывод -print0 используют в паре с xargs -0 (по странице man).
6️⃣ Удаление файлов через find и опции
Перед удалением сначала выполните те же условия без удаления и своими глазами проверьте список файлов на удаление.
find /root/work -name '*.tmp'/root/work/docs/old.tmp
/root/work/logs/cache.tmp-exec rm {} \; запускает rm для каждого найденного файла. Запуск только в папке logs удалил cache.tmp, и остался только app.log.
find /root/work/logs -name '*.tmp' -exec rm {} \;ls /root/work/logsapp.log-delete заставляет find удалять напрямую, без запуска rm. При запуске ничего не вывелось, а повторный поиск с тем же условием файлов .tmp уже не нашёл.
find /root/work -name '*.tmp' -deletefind /root/work -name '*.tmp'-delete обязательно пишется после условий. find вычисляет командную строку с начала как одно выражение, поэтому с -delete в самом начале он пытается удалить всё под начальным путём, ещё не посмотрев на условия (по странице man).
🔲 Опции команды find
📌 Основные опции: результаты из этой статьи
| Опция | Назначение | Результат в этой статье | |
|---|---|---|---|
| -name | Находит имена, подходящие под шаблон (учитывает регистр) | '*.txt' даёт пять файлов, 'readme*' только docs/readme.txt | |
| -iname | Сравнивает имена без учёта регистра | 'readme*' даёт docs/readme.txt и README.md | |
| -type f, -type d | Оставляет только обычные файлы (f) или папки (d) | -type d даёт четыре папки с начальным путём, -type f десять файлов | |
| -maxdepth | Спускается только на N уровней под начальный путь | -maxdepth 1 оставляет только пять файлов прямо под /root/work | |
| -mindepth | Пропускает уровни выше N от начального пути | -mindepth 2 даёт только пять файлов во вложенных папках | |
| -size | Ищет по размеру (+ больше, - меньше, единицы c, k, M, G) | -size +1k даёт три файла, -size -1k только empty.txt | |
| -mtime | Сравнивает время с изменения содержимого в единицах по 24 часа | -mtime -1 даёт пять файлов, -mtime +7 два файла .tmp | |
| -mmin | Сравнивает время с изменения в минутах | -mmin -60 даёт те же пять файлов, что и -mtime -1 | |
| -ctime | Сравнивает в единицах по 24 часа вплоть до смены данных файла, например прав и имени | -ctime -1 даёт все десять тестовых файлов | |
| -newer | Находит то, что изменено позже опорного файла | Пять файлов с README.md, заданным на 3 дня назад, в роли опорного | |
| -empty | Находит файлы размером 0 и пустые папки | Даёт пустую папку backup и empty.txt | |
| -perm | Ищет по правам (только число: ровно это значение, - впереди: все указанные, / впереди: хотя бы одно) | -perm 644 даёт восемь файлов, -perm -u+x даёт run.sh, а -perm /6000 после SUID даёт run.sh | |
| -user | Фильтрует по имени владельца | С -name '*.log' даёт app.log, несуществующая учётная запись завершается ошибкой | |
| -nouser | Находит то, для UID владельца чего нет учётной записи | Даёт guide.txt с UID, изменённым на 1500 | |
| -nogroup | Находит то, для GID группы чего нет группы | Даёт guide.txt с GID, изменённым на 1500 | |
| -o | Оставляет то, что подходит под одно из двух (в скобках вместе с другими условиями) | Группировка .log и .tmp даёт три файла | |
| ! | Обращает условие сразу после него | Даёт пять файлов, не являющихся .txt | |
| -exec | Запускает команду после него для каждого найденного файла (путь вместо {}) | С + в конце один echo выводит пять путей одной строкой | |
| -print0 | Ставит между путями нулевой символ вместо перевода строки | Нулевой символ не виден, пути идут слитно в одну строку | |
| -delete | find сам удаляет найденные файлы (пишется после условий) | Ничего не вывелось, повторный поиск не нашёл файлов .tmp |
📎 Другие опции: по официальному руководству
| Опция | Назначение | |
|---|---|---|
| -L | Следует по символическим ссылкам и использует данные целевого файла | |
| -path | Сравнивает весь путь, а не имя, с шаблоном оболочки | |
| -regex | Сравнивает весь путь с регулярным выражением | |
| -iregex | Сравнивает как -regex, но без учёта регистра | |
| -atime | Сравнивает время с последнего доступа в единицах по 24 часа | |
| -cnewer | Находит то, чей статус сменился позже времени изменения опорного файла | |
| -group | Фильтрует по имени группы-владельца | |
| -xdev | Не спускается в папки других файловых систем | |
| -daystart | Считает условия времени с 0 часов сегодня, а не от 24 часов назад | |
| -prune | Если это папка, не спускается в неё | |
| -quit | Сразу завершает работу (-print -quit выводит только первый результат) | |
| -printf | Выводит в заданном вами формате | |
| -ls | Выводит найденные файлы в формате ls -dils | |
| -fprint | Сохраняет найденные пути в указанный файл вместо экрана | |
| -ok | Работает как -exec, но сначала спрашивает | |
| -execdir | Работает как -exec, но запускает команду в папке найденного файла |
🔲 Симулятор команды find
Симулятор ниже это виртуальный сервер с той же тестовой папкой, где можно самим выполнять условия -name, -type, -size, -mtime, -perm, а также -exec и -delete.
7️⃣ Ошибки команды find
Если написать шаблон без кавычек, оболочка применяет правила для Подстановочные знаки и сначала раскрывает его в имена файлов текущей папки. Когда мы перешли в папку docs и написали *.txt без кавычек, оболочка передала два имени guide.txt и readme.txt, и появилась ошибка ниже.
cd /root/work/docsfind /root/work -name *.txtfind: paths must precede expression: `readme.txt'
find: possible unquoted pattern after predicate `-name'?Код выхода был 1.
Без \; или + в конце -exec появилось [missing argument to `-exec’].
find /root/work -name '*.log' -exec ls -l {}find: missing argument to `-exec'Несуществующий начальный путь даёт [No such file or directory] и завершается с кодом выхода 1.
find /nonexistent-dir -name xfind: '/nonexistent-dir': No such file or directoryНесуществующее имя учётной записи в -user или значение -type, отличное от заданных букв вроде f и d, сразу завершаются ошибкой, и поиск не начинается.
find /root/work -user nosuchuserfind: 'nosuchuser' is not the name of a known userfind /root/work -type xfind: Unknown argument to -type: xПод обычной учётной записью на сервере Debian 12 мы изменили права папки lock в тестовой папке на 000 и выполнили поиск по тестовой папке. Содержимое lock не читалось, появилось [Permission denied], и код выхода был 1.
find ~/findtest/home/user/findtest
/home/user/findtest/report.txt
/home/user/findtest/lock
find: '/home/user/findtest/lock': Permission denied
/home/user/findtest/docs
/home/user/findtest/docs/readme.txtЧтобы скрыть только строки ошибок, добавьте 2>/dev/null. Текст ошибки исчез, но код выхода остался 1.
find ~/findtest -name '*.txt' 2>/dev/null/home/user/findtest/report.txt
/home/user/findtest/docs/readme.txt🔢 FAQ & рекомендуемые материалы
Поиск по содержимому
Команда grep в Linux
Статья о команде grep, которая ищет текст в файлах и выводит эти строки, и о её опциях.
Удаление
Команда rm в Linux: опции rm -rf и удаление без подтверждения
Статья об опциях -r и -f и запросах подтверждения команды rm при удалении файлов и папок.
Место на диске
Команда du: проверка места на диске
Статья о подсчёте места на диске, занятого папками и файлами, с помощью команды du.
ℹ️ О партнёрских ссылках
Материалы этого сайта содержат партнёрские ссылки. Если посетитель покупает товар или услугу по такой ссылке, сайт получает комиссию от продавца. При этом сумма, которую платит покупатель (при акционных скидках сумма снижается ↓), не увеличивается. Указанные цены, скидки и наличие актуальны на момент написания и могут отличаться, поэтому перед покупкой уточняйте их у продавца. Товары выбираются и оцениваются по нашим собственным критериям, а комиссия не влияет ни на порядок, ни на содержание обзоров.