Гид по технологиям

Запуск команд рекурсивно по дереву директорий в Linux

8 min read Linux Обновлено 26 Dec 2025
Рекурсивный запуск команд по дереву директорий в Linux
Рекурсивный запуск команд по дереву директорий в Linux

Ноутбук с Linux и приглашением bash

Быстрые ссылки

  • All About Directories

  • The tree Command

  • Don’t Parse the Output From ls to Traverse Directories

  • Using the find Command

  • Traversing Directory Trees With a Script

  • Recursion Is Weird

Что такое директории и зачем обходить их рекурсивно

Директории в Linux позволяют логически группировать файлы и разделять работу по папкам. Часто требуется выполнить однотипную операцию: посчитать строки во всех текстах, поменять права, сохранить копию, удалить временные файлы и т. п. Ручной переход cd в каждую папку утомителен и ненадёжен — лучше автоматизировать процесс.

Определение: «Рекурсивный обход» — это алгоритм, который посещает директорию и затем последовательно посещает все её поддиректории.

Понятие «дерева директорий» помогает мыслить и проектировать операции: каждая директория — узел дерева, в котором хранятся файлы и дочерние узлы.

Быстрое напоминание по cd и относительным путям

  • cd — перейти в домашнюю директорию без аргументов.
  • cd ~ — явный переход в домашнюю директорию.
  • cd .. — подняться на уровень выше.

Это полезно для навигации, но не решает проблему выполнения одной и той же команды для каждой ветки дерева.

Команда tree — визуализация структуры

Команда tree не выполняет команды по дереву, но наглядно показывает структуру директорий, что помогает планировать обходы.

Установка:

Ubuntu:

sudo apt install tree

Fedora:

sudo dnf install tree

Manjaro:

sudo pacman -Sy tree

Запуск без параметров выводит дерево текущей директории:

tree

Запуск tree в текущей директории

Можно передать путь:

tree work

Запуск tree для указанной директории

Показывать только директории:

tree -d work

Запуск tree, показывать только директории

Применение: сначала изучите дерево, затем выберите стратегию обхода.

Никогда не парсите вывод ls для обхода директорий

Вывод ls предназначен для человека, а не для програмного разбора. Файловые имена могут содержать пробелы, переводы строк, табы, кавычки и другие символы, что делает парсинг хрупким.

Пример опасного имени директории:

Странное имя директории

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

Замечание: команды find, xargs -0, и возможности shell (globbing с осторожностью) — более надёжные варианты.

find — основной инструмент для рекурсивных действий

Команда find умеет рекурсивно искать файлы/директории и запускать команды для каждого результата.

Простой пример: вывести все найденные директории и выполнить команду в каждой (печать имени):

find work -type d -execdir echo "In:" {} \;

Использование find для рекурсивного поиска директорий

Как работают части команды:

  • find — сама команда поиска.
  • work — стартовая директория (может быть и путь типа /var/log).
  • -type d — искать только директории.
  • -execdir — выполнить команду внутри найденной директории (безопаснее, чем -exec при работе с относительными файлами).
  • echo “In:” {} — команда; {} заменяется на имя найденной записи.
  • \; — завершение выражения; точка с запятой экранируется чтобы оболочка не интерпретировала её.

Поиск файлов по имени и выполнение команды:

find work -name "*.txt" -type f -execdir echo "Found:" {} \;

Использование find для рекурсивного поиска файлов

Посчитать строки во всех .txt файлах:

find work -name "*.txt" -type f -execdir wc -l {} \;

Использование find с командой wc

Советы по find:

  • -execdir выполняет команду в каталоге, где найден файл — это безопаснее для относительных путей.
  • -exec … {} + — собирает много аргументов в один вызов команды (похож на xargs).
  • Используйте -print0 и xargs -0 для безопасной передачи имён с нулевым байтом.

Примеры с xargs:

find work -type f -name "*.log" -print0 | xargs -0 gzip

Это сожмёт все .log файлы, корректно обрабатывая имена с пробелами и спецсимволами.

Когда find не подходит — альтернативы и расширения

  • GNU parallel — параллельный запуск команд над списком файлов. Хорош для ускорения при многоядерных системах.
  • rsync — для массовых копий/синхронизации и фильтрации по шаблонам.
  • fd (замена find с более удобным синтаксисом) — быстрее и более человекочитаемо.
  • python/perl/ruby — если нужна сложная логика обработки на каждом файле.

Пример с parallel:

find work -type f -name "*.jpg" -print0 | parallel -0 mogrify -resize 800x800 {}

Parallel запустит mogrify параллельно по изображениям.

Рекурсивный скрипт на bash — когда нужно полное управление

Иногда хочется реализовать логику вручную: например, менять поведение в зависимости от глубины, агрегировать данные в процессе обхода или иметь сложные условия. Ниже — пример скрипта, который обходится без опасного парсинга и корректно работает с именами, включающими пробелы и точки.

Сохраните как recurse.sh:

#!/bin/bash

shopt -s dotglob nullglob

function recursive {

  local current_dir dir_or_file

  for current_dir in "$1"; do

    echo "Directory command for:" "$current_dir"

    for dir_or_file in "$current_dir"/*; do

      if [[ -d $dir_or_file ]]; then

        recursive "$dir_or_file"

      else

        wc "$dir_or_file"

      fi

    done

  done

}

recursive "$1"

Разбор ключевых моментов:

  • shopt -s dotglob nullglob — включает скрытые файлы (dotfiles) и предотвращает расширение шаблонов в литералы, если совпадений нет.
  • Все переменные окружены кавычками “…” при использовании, чтобы корректно обрабатывать пробелы.
  • Рекурсивный вызов recursive “$dir_or_file” — стандартная техника обхода “в глубину”.

Сделать исполняемым:

chmod +x recurse.sh

Запуск:

./recurse.sh work

Обработка директорий от мелких к большим

Этот конкретный скрипт выполняет команду в директории до обработки внутренних файлов (pre-order). Если вы хотите обратный порядок (post-order), переместите строку с “Directory command for:” после внутреннего цикла.

Пример: пост-обработка директорий (обрабатывать сначала вложения)

#!/bin/bash

shopt -s dotglob nullglob

function recursive {

  local current_dir dir_or_file

  for current_dir in "$1"; do

    for dir_or_file in "$current_dir"/*; do

      if [[ -d $dir_or_file ]]; then

        recursive "$dir_or_file"

      else

        wc "$dir_or_file"

      fi

    done

    echo "Directory command for:" "$current_dir"

  done

}

recursive "$1"

Обработка директорий от глубоких к поверхностным

Практические советы по безопасности и надёжности

Important: любые операции, которые изменяют или удаляют файлы (rm, mv, chown, chmod), нужно предварительно прогонять в режиме “песочницы” — выводить список команд без выполнения или делать dry-run.

Рекомендации:

  • Всегда сначала запускайте find … -print или скрипт в режиме echo, чтобы увидеть список целей.
  • Используйте контроль версий/резервное копирование при массовых операциях.
  • Перед командами удаления добавляйте подтверждение: rm -i или собственную логику подтверждения.
  • Учитывайте символические ссылки: добавляйте -P/-L опции или проверяйте их, чтобы не выйти за пределы ожидаемой области.

Безопасность: избегайте выполнения результатов поиска под root без крайней необходимости. Если нужно, тестируйте под обычным пользователем, затем переходите к escalated действиям.

Когда подходы не работают — ограничения и контрпримеры

  • find не может корректно обработать удалённые файлы в сетевых файловых системах при временных сбоях — операции могут фейлиться.
  • Если структура очень «глубокая» (сотни тысяч уровней), рекурсивный bash-скрипт может столкнуться с ограничением стека — лучше использовать итеративный подход или специализированный инструмент.
  • Параллелизация может привести к перегрузке IO: если каждая задача интенсивно пишет на диск, параллельный запуск замедлит систему.

Как выбрать метод — мини-методология

  1. Определите задачу: чтение/подсчёт/изменение/удаление/копирование.
  2. Если нужна только выборка — используйте find с флагами и фильтрами.
  3. Если требуется изменение множественных файлов без сложной логики — find + -exec … + или find | xargs.
  4. Для CPU-bound задач применяйте GNU parallel.
  5. Для сложной логики уровня “если … то … иначе” — пишите скрипт (bash, python).
  6. Всегда начните с dry-run.

Шаблоны и сниппеты (cheat sheet)

  • Безопасный поиск и печать списка директорий:
find /path/to/start -type d -print
  • Запуск команды в каждой директории (внутри директории):
find . -type d -execdir bash -c 'pwd; ls -la' \;
  • Выполнение на множествах файлов в одном вызове (экономит вызовы команды):
find . -type f -name "*.log" -exec gzip {} +
  • Использование null-terminated строк с xargs:
find . -type f -print0 | xargs -0 -n1 -P4 some-command
  • Быстрый alias для повседневных задач (в .bashrc):
alias findtxt='find . -type f -name "*.txt"'

Чек-листы по ролям

Для разработчика:

  • Проверить список файлов через find -print
  • Выполнить dry-run скрипта (echo вместо реального действия)
  • Запустить в тестовой ветке данных
  • Запустить автоматические тесты после изменений

Для системного администратора:

  • Запланировать окно обслуживания при массовых изменениях
  • Сделать резервную копию перед массовыми операциями
  • Использовать –noop/–dry-run если доступно
  • Ограничить параллелизм чтобы не перегружать IO

Критерии приёмки

  • Список целей (файлов/директорий) совпадает с ожидаемым на dry-run.
  • Команды выполнены без ошибок для 99% элементов (лог ошибок при необходимости).
  • Операция откатывается (rollback) или имеется бэкап в случае критических изменений.

Тестовые случаи и приёмочные проверки

  1. Файлы с пробелами в именах — должны обрабатываться корректно.
  2. Скрытые файлы и директории — включаются при shopt dotglob или соответствующих флагах.
  3. Симлинки — тестировать поведение: следовать/не следовать.
  4. Большое количество файлов — проверка на производительность и память.

Пример runbook (быстрый сценарий восстановления)

  1. Если команда удалила файлы по ошибке, остановите дальнейшие операции.
  2. Если есть snapshot/резервная копия — восстановите из неё в отдельную папку и сравните.
  3. Проанализируйте логи запуска (если записывали вывод команд).
  4. Внедрите проверочные тесты (dry-run) в CI чтобы подобных инцидентов не допускать.

Ментальные модели и эвристики

  • “Pre-order vs Post-order”: решите, нужно ли вам сначала выполнить действие в каталоге, а потом в его детях (pre-order), или наоборот (post-order).
  • “Fail fast”: на больших деревьях лучше сначала проверять условия и пробегать dry-run, чтобы быстро увидеть ошибки.
  • “Least privilege”: не выполнять массовые операции от root без явной причины.

Пример альтернатив на Python (если bash не удобен)

Минимальный скрипт, использующий os.walk — подходит для более сложной логики и лучшей обработки ошибок:

#!/usr/bin/env python3

import os
import sys

root = sys.argv[1]

for dirpath, dirnames, filenames in os.walk(root):
    print('In:', dirpath)
    for fname in filenames:
        path = os.path.join(dirpath, fname)
        # пример: посчитать байты
        print('File:', path, os.path.getsize(path))

Python удобен для сложных трансформаций и запуска внешних программ с subprocess.

Когда рекурсия становится плохой идеей

  • Стековый overflow: очень глубоко вложенные структуры могут вызвать проблемы в рекурсивных решениях в некоторых языках.
  • Требуется транзакционная атомарность над множеством объектов — лучше использовать механизмы БД или специализированные инструменты.

Итог и рекомендации

  • Для большинства задач используйте find и -execdir или xargs -0: это просто, безопасно и быстро внедряется в существующие скрипты.
  • Для параллельных задач применяйте GNU parallel или xargs -P, но учитывайте нагрузку на диск.
  • Для сложной логики используйте скрипт на bash или высокоуровневом языке (Python), тщательно тестируйте и начинайте с dry-run.

Кратко: выберите инструмент под задачу, сначала смоделируйте и прогоните в режиме показа, затем выполняйте настоящие операции с бэкапом и контролем привилегий.

Summary:

  • Используйте безопасные методы (find, xargs -0, -execdir).
  • Не парсите ls.
  • Тестируйте в dry-run.
  • При массовых изменениях обеспечьте резервную копию.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Запуск ROM и игр через Boxee — настройка лаунчера
Инструкции

Запуск ROM и игр через Boxee — настройка лаунчера

Установка USB-розетки в стену — пошагово
Электрика

Установка USB-розетки в стену — пошагово

Запись экрана в Windows 10 через Xbox Game Bar
Windows

Запись экрана в Windows 10 через Xbox Game Bar

Тёмная тема Outlook на мобильных
Руководство

Тёмная тема Outlook на мобильных

Как отвечать на вопросы о soft skills
Карьера

Как отвечать на вопросы о soft skills

Избежать подписки для умного дверного звонка
Умный дом

Избежать подписки для умного дверного звонка