Python find directory: системное администрирование файловых каталогов

Раздел: Администрирование -> Работа с файлами

Основные методы поиска директорий в Python

Как найти все папки с заданным именем рекурсивно, используя современный подход?

Наиболее эффективное и рекомендуемое решение - модуль pathlib с методом rglob(). Он возвращает объекты Path, поддерживает фильтрацию по имени и работает одинаково на всех платформах.

from pathlib import Path

def find_directories_by_name(root_path, target_name):
    root = Path(root_path)
    if not root.is_dir():
        return []
    # rglob("*") находит все, затем фильтруем по is_dir() и имени
    return [p for p in root.rglob("*") if p.is_dir() and p.name == target_name]

# Пример использования
result = find_directories_by_name("/home/user", "logs")
for d in result:
    print(d)

путь к path python (путь к python (переменная path))

Пояснение: Path.rglob("*") обходит всё дерево рекурсивно. Фильтр p.is_dir() оставляет только каталоги, а сравнение имени решает задачу поиска. Метод безопасен для путей с пробелами и не зависит от локали.

Возможные проблемы и их решения:

  • PermissionError - при отсутствии прав доступа к некоторым папкам. Обработка исключения: обернуть в try/except или использовать Path.is_dir() с проверкой доступа.
  • Символические ссылки - rglob по умолчанию следует за ними, что может привести к бесконечным циклам. Для предотвращения используйте Path.resolve(strict=False) или проверяйте, не пройдена ли папка ранее.
  • Большое количество файлов - rglob загружает всё дерево в память. Альтернатива - итератор Path.glob("**/target_name") с условием, что target_name - точное имя.

Как найти директории, используя классический os.walk?

Модуль os с функцией walk() даёт полный контроль над обходом и позволяет обрабатывать ошибки доступа через параметр onerror.

import os

def find_dirs_os_walk(root_path, target_name):
    matches = []
    for dirpath, dirnames, filenames in os.walk(root_path):
        for d in dirnames:
            if d == target_name:
                matches.append(os.path.join(dirpath, d))
    return matches

print(find_dirs_os_walk("/var", "cache"))

Python pathlib path (использование pathlib в python)

Проблемы:

  • Ошибки доступа - os.walk по умолчанию пропускает недоступные папки, но не сообщает об этом. Для логирования используйте параметр onerror.
  • Производительность - os.walk создаёт списки всех каталогов и файлов на каждом уровне, что может быть затратно при глубокой вложенности.

Как выполнить поиск с помощью glob и fnmatch?

Модуль glob поддерживает шаблоны с подстановочными знаками (*, ?, [abc]). Для рекурсивного поиска используйте ** (Python 3.5+).

import glob

def find_dirs_glob(pattern):
    # pattern может быть, например, "**/logs"
    return [p for p in glob.glob(pattern, recursive=True) if os.path.isdir(p)]

# Пример
print(find_dirs_glob("/home/**/logs"))

Path python 2 (путь к python 2)

Пояснение: glob.glob с recursive=True обходит все поддиректории. Ограничение - нельзя задать глубину поиска. Для фильтрации по типу объекта приходится дополнительно вызывать os.path.isdir.

Типичная ошибка:

Если в шаблоне не указать **, поиск будет только в текущей папке. Также glob не обнаруживает символические ссылки на каталоги по умолчанию.

Как воспользоваться утилитой find из командной строки через subprocess?

Для системных администраторов иногда проще вызвать системную команду find, особенно когда нужны дополнительные критерии (размер, время, тип).

import subprocess

def find_with_shell(root, name):
    cmd = ["find", root, "-type", "d", "-name", name]
    result = subprocess.run(cmd, capture_output=True, text=True, check=False)
    if result.returncode != 0:
        # Обработка ошибок
        return []
    return result.stdout.strip().split('\n') if result.stdout else []

print(find_with_shell("/etc", "conf.d"))

Python site packages path (путь к site-packages в python)

Данный подход использует системный find, который очень быстр и гибок. Однако он не кроссплатформенный (нет в Windows).

Проблемы:

  • Зависимость от ОС - команда может отсутствовать или иметь другой синтаксис.
  • Безопасность - при передаче пользовательского ввода в shell необходимо экранировать (использовать список аргументов).
  • Кодировка вывода - в Windows или при нестандартных локальных настройках возможны ошибки декодирования.

Как искать директории по содержимому файлов внутри них?

Иногда требуется найти папки, в которых есть файл с определённым содержимым. Комбинируем поиск каталогов и проверку содержимого.

from pathlib import Path

def find_dirs_containing_text(root, filename, search_text):
    result = []
    for dir_path in Path(root).rglob("*"):
        if dir_path.is_dir():
            target_file = dir_path / filename
            if target_file.exists() and target_file.is_file():
                try:
                    content = target_file.read_text(encoding='utf-8', errors='ignore')
                    if search_text in content:
                        result.append(str(dir_path))
                except Exception:
                    pass
    return result

# Пример
print(find_dirs_containing_text("/var/log", "error.log", "CRITICAL"))

Сложность:

При большом количестве файлов чтение каждого снижает производительность. Рекомендуется сначала отфильтровать каталоги по имени, а затем проверять содержимое.

- Python текущая директория (получение текущей рабочей директории в python)
- File does not exist python (проверка существования файла)
- Python path configuration (конфигурация путей python)

Расширенные примеры поиска директорий с пояснениями

1. Поиск с ограничением глубины рекурсии

Иногда нужно искать только на определённой глубине, например, не глубже 3 уровней. Реализация на pathlib:

Пример
from pathlib import Path

def find_dirs_depth_limit(root_path, target_name, max_depth):
    root = Path(root_path)
    result = []
    for p in root.rglob("*"):
        # относительный путь от root, считаем глубину по количеству разделителей
        rel = p.relative_to(root)
        depth = len(rel.parts)
        if depth > max_depth:
            continue
        if p.is_dir() and p.name == target_name:
            result.append(p)
    return result

print(find_dirs_depth_limit("/tmp", "data", 2))
[PosixPath('/tmp/data'), PosixPath('/tmp/sub/data')] # не глубже 2

2. Поиск с исключением скрытых каталогов (начинающихся с точки)

В Unix-системах скрытые папки начинаются с точки. Их можно исключить:

Пример
from pathlib import Path

def find_visible_dirs(root, name):
    result = []
    for p in Path(root).rglob("*"):
        # Проверяем каждый компонент пути на скрытость
        if any(part.startswith('.') for part in p.parts):
            continue
        if p.is_dir() and p.name == name:
            result.append(p)
    return result

print(find_visible_dirs("/home/user", "projects"))
[PosixPath('/home/user/projects')]

3. Поиск директорий с наибольшим размером (через os.walk)

Иногда нужно найти самые «тяжёлые» каталоги. Собираем размер всех папок рекурсивно:

Пример
import os

def get_directory_size(path):
    total = 0
    for dirpath, dirnames, filenames in os.walk(path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            try:
                total += os.path.getsize(fp)
            except OSError:
                pass
    return total

def largest_dirs(root, top_n=5):
    dirs = []
    for entry in os.scandir(root):
        if entry.is_dir():
            try:
                size = get_directory_size(entry.path)
                dirs.append((entry.path, size))
            except PermissionError:
                continue
    dirs.sort(key=lambda x: x[1], reverse=True)
    return dirs[:top_n]

for path, size in largest_dirs("/var", 3):
    print(f"{path}: {size / (1024**3):.2f} GB")
/var/log: 1.23 GB
/var/cache: 0.87 GB
/var/lib: 0.45 GB

4. Параллельный поиск с использованием ThreadPoolExecutor

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

Пример
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path

def search_in_subdir(subdir, target_name):
    return [p for p in subdir.rglob("*") if p.is_dir() and p.name == target_name]

def parallel_find(root, target_name, max_workers=4):
    root_path = Path(root)
    subdirs = [d for d in root_path.iterdir() if d.is_dir()]
    results = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(search_in_subdir, d, target_name): d for d in subdirs}
        for future in as_completed(futures):
            results.extend(future.result())
    return results

print(parallel_find("/home", "backup"))

Важно: при параллельном поиске файловая система может стать узким местом, поэтому прирост производительности не всегда значителен.

5. Поиск директорий с помощью os.scandir и ручной рекурсии

Использование os.scandir даёт более эффективный обход, чем os.walk, поскольку не загружает сразу все имена:

Пример
import os

def find_dirs_scandir_recursive(root, target_name):
    result = []
    try:
        with os.scandir(root) as it:
            for entry in it:
                if entry.is_dir(follow_symlinks=False):
                    if entry.name == target_name:
                        result.append(entry.path)
                    # рекурсивный вызов
                    result.extend(find_dirs_scandir_recursive(entry.path, target_name))
    except PermissionError:
        pass
    return result

print(find_dirs_scandir_recursive("/opt", "config"))
['/opt/app/config', '/opt/app2/config']

6. Поиск с учётом времени последнего изменения (mtime)

Найти папки, изменённые за последние N дней:

Пример
from pathlib import Path
import time

def find_dirs_modified_recently(root, days):
    cutoff = time.time() - days * 86400
    result = []
    for p in Path(root).rglob("*"):
        if p.is_dir():
            stat = p.stat()
            if stat.st_mtime > cutoff:
                result.append(p)
    return result

print(find_dirs_modified_recently("/tmp", 7))

Обратите внимание: изменение самого каталога (добавление/удаление файлов) обновляет mtime папки не всегда, поэтому лучше дополнительно проверять содержимое.

7. Поиск и вывод полного пути с форматированием

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

Пример
from pathlib import Path

def find_and_format(root, name):
    found = [str(p) for p in Path(root).rglob("*") if p.is_dir() and p.name == name]
    if not found:
        return "Ничего не найдено"
    lines = ["Найденные директории:"]
    for i, path in enumerate(found, 1):
        lines.append(f"{i}. {path}")
    return '\n'.join(lines)

print(find_and_format("/home", "logs"))
Найденные директории:
1. /home/user/logs
2. /home/user/project/logs

Поиск директории - comments

En
Python find directory (python)