|
Как в команде find исключить из поиска определенную папку?
Чтобы исключить папку из поиска в команде find, используйте параметр -path в сочетании с -prune.
Вот самый распространенный и правильный способ (искл_папка в пути поиска не будет просматриваться):
find /путь/поиска -path "/путь/поиска/искл_папка" -prune -o -name "*.txt" -print
Как это работает:
-path "/путь/поиска/искл_папка" — находит полный путь к папке, которую нужно исключить. -prune — говорит find не заходить внутрь этой папки (обрубает ветку). -o — логическое ИЛИ. Если условие с -path и -prune сработало, то остальная часть команды (-name) игнорируется. Если нет — выполняется поиск по имени.
Выглядит сложно, но ничего не поделаешь - этим мы платим за универсальность инструмента find.
Конкретные примеры:
1. Исключаем папку `cache` в текущей директории:
find . -path "./cache" -prune -o -name "*.log" -print
2. Исключаем папку на любом уровне вложенности (например, все папки `node_modules`):
find . -path "*/node_modules" -prune -o -name "*.js" -print
3. Исключаем сразу несколько папок (объединяем через `-o`):
find . \( -path "./tmp" -o -path "./backup" \) -prune -o -name "*.txt" -print
Важный нюанс (частая ошибка). Если вы пишете:
find . -path "./cache" -prune -name "*.log" # ❌ НЕПРАВИЛЬНО
... то find найдет файлы *.log только внутри папки cache, а не снаружи.
Чтобы искать снаружи, всегда используйте конструкцию с -o и обязательно ставьте `-print` в конце:
find . -path "./cache" -prune -o -name "*.log" -print # ✅ ПРАВИЛЬНО
Альтернативный способ исключения папки (через grep -v). Если вам нужно исключить папку, но она находится глубоко и с -prune разобраться сложно, можно отфильтровать результат через grep:
find . -name "*.txt" | grep -v "./cache/"
Минус: этот способ медленнее, так как find всё равно сначала обойдет всю папку cache (это может быть долго, если папка большая), а потом просто скроет результат. -prune же физически пропускает обход, что намного быстрее.
[Как для find исключить несколько папок?]
Для исключения нескольких папок в find нужно объединить условия через -o (логическое ИЛИ) в группах с -prune. Группа определяется скобками, которые нужно экранировать символом '\'.
Вот универсальный шаблон:
find . \( -path "./папка1" -o -path "./папка2" -o -path "./папка3" \) -prune -o -name "*.txt" -print
Ключевые моменты:
\( ... \) — группировка условий. -o — операция ИЛИ (исключаем, если путь совпадает с любой из перечисленных папок). После -prune обязательно ставим -o и -print (или другое действие).
Конкретные примеры:
1. Исключаем из поиска папки cache, tmp и backup в текущей директории:
find . \( -path "./cache" -o -path "./tmp" -o -path "./backup" \) -prune -o -name "*.log" -print
2. Исключаем папки на любом уровне вложенности (например, все node_modules, .git и dist):
find . \( -path "*/node_modules" -o -path "*/.git" -o -path "*/dist" \) -prune -o -name "*.js" -print
3. Исключаем папки с пробелами или спецсимволами в имени (с помощью двойных кавычек):
find . \( -path "./My Documents" -o -path "./Downloads" \) -prune -o -name "*.pdf" -print
Если папок много — используйте переменную или файл.
Вариант A: список в переменной окружения (через -o):
exclude_dirs="-path ./cache -o -path ./tmp -o -path ./backup" find . \( $exclude_dirs \) -prune -o -name "*.txt" -print
Вариант B: через bash-массив (удобно, если пути сложные):
exclude=("./cache" "./tmp" "./backup") # Собираем условие: for dir in "${exclude[@]}"; do args+=(-path "$dir" -o) done # Убираем последний -o unset 'args[${#args[@]}-1]'
find . \( "${args[@]}" \) -prune -o -name "*.txt" -print
Вариант C: читаем список из файла (по одной папке на строку):
# files.txt содержит: # ./cache # ./tmp # ./backup
find . $(awk '{print "-path " $1 " -o"}' files.txt | sed '$ s/-o$//') -prune -o -name "*.txt" -print
Альтернатива (проще, но медленнее). Если папок много и производительность не критична, можно отфильтровать через `grep -v` с опцией `-E` (расширенными регулярками):
find . -name "*.txt" | grep -v -E "(./cache|./tmp|./backup)"
Или исключить папки на любом уровне:
find . -name "*.txt" | grep -v -E "(node_modules|\.git|dist)"
Минус: find всё равно физически обходит исключённые папки, что может быть медленно для больших директорий (например, node_modules). Если использовать -prune, то будет быстрее, так как пропускает обход.
Частая ошибка исключения папок, так делать неправильно:
find . -path "./cache" -prune -o -path "./tmp" -prune -o -name "*.txt" -print
Работать будет, но если папок много — код становится громоздким и нечитаемым. Лучше группировать все условия в одни скобки перед одним -prune.
Правильно:
find . \( -path "./cache" -o -path "./tmp" \) -prune -o -name "*.txt" -print
|