linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Команды/xargs-and-find-exec

kb/commands ── Команды ── intermediate

xargs и find -exec - массовые операции

Два способа применить команду к набору файлов: `find ... -exec cmd {} +` (внутри find) и `... | xargs cmd` (через pipe). Для безопасности с пробелами/спецсимволами - связка `find -print0 | xargs -0`.

view as markdownaka: xargs, find-exec, find-print0, xargs-0, parallel-xargs

Зачем не просто loop в bash

Допустим хочешь сжать все .log старше 7 дней. На bash напрямую:

bash
for f in $(find /var/log -name '*.log' -mtime +7); do
    gzip "$f"
done

Проблемы:

  • Word splitting на пробелах - файл my app.log распадётся на два аргумента.
  • Команда форкается отдельно для каждого файла - медленно при тысячах.
  • Bash может не справиться с длинной командной строкой при огромном выводе find.

find -exec и xargs решают всё это - они корректно работают с пробелами, батчат вызовы и грамотно обходят ARG_MAX.

find -exec - два варианта

\; - один-в-один

bash
find /var/log -name '*.log' -mtime +7 -exec gzip {} \;

Запускает gzip отдельно для каждого файла. {} подставляется на путь. \; - литеральная точка с запятой (надо экранировать от bash).

Минус: 1000 файлов = 1000 fork'ов = медленно.

+ - батч

bash
find /var/log -name '*.log' -mtime +7 -exec gzip {} +

Накапливает аргументы и вызывает gzip один раз с пачкой файлов (или несколько раз если упёрлось в ARG_MAX). На порядок быстрее.

Используй + всегда когда команда поддерживает множество файлов (gzip, rm, chmod, chown, cp). Используй \; только когда нужен один файл за раз (например shell-конструкция с условием).

xargs - pipe-вариант

bash
find /var/log -name '*.log' -mtime +7 | xargs gzip

Эквивалент find ... -exec gzip {} +. По умолчанию xargs батчит и сам следит за ARG_MAX.

Зачем оба способа существуют: xargs берёт ввод из любого pipe'а (не только find), и имеет больше опций (параллельность, подстановка в произвольное место). find -exec короче для простых случаев.

ВАЖНО: -print0 / -0 для безопасности

Дефолтный разделитель xargs - whitespace. Файл foo bar.log он разрежет пополам и попытается обработать foo и bar.log как два пути. Кавычки тоже трактуются специально.

Решение - null-byte как разделитель:

bash
find /var/log -name '*.log' -print0 | xargs -0 gzip
  • find -print0 - печатает имена через \0 вместо \n.
  • xargs -0 - читает разделённое \0.

Имя файла не может содержать \0 (это терминатор C-строк), поэтому такой пайп всегда корректен.

Правило: если ты не уверен на 100% что в именах нет пробелов/кавычек - всегда -print0 | xargs -0. Или find -exec ... +.

xargs - практичные опции

ФлагЧто делает
-0разделитель - \0 (для find -print0)
-I {}подставлять {} в произвольное место в команде
-n Nпо N аргументов на вызов
-P Nдо N параллельных процессов
-r / --no-run-if-emptyне запускать команду если ввод пустой
-ttrace - печатать команду перед выполнением
--max-args=1то же что -n 1
-d $'\n'свой разделитель (например только newline)

Подстановка в середину - -I

bash
ls *.tar.gz | xargs -I {} mv {} /backup/{}.bak
# или
cat hosts.txt | xargs -I HOST ssh HOST 'uptime'

По умолчанию xargs кладёт аргументы в конец команды. С -I подставит туда где написал плейсхолдер. Подходит когда нужен файл и до и после (например mv X /dst/X.bak).

Цена: -I неявно ставит -n 1 - вернулись к одному вызову на аргумент.

Параллельность - -P

bash
find . -name '*.log' -print0 | xargs -0 -n1 -P4 gzip

▸4 параллельных gzip'а

Полезно когда задача CPU-bound и есть несколько ядер. Для I/O-bound можно ставить -P больше числа ядер.

Внимание: при -P > 1 порядок вывода непредсказуем - несколько процессов пишут в stdout вперемешку.

Batch size - -n

bash
echo {1..100} | xargs -n 5 echo

▸echo 1 2 3 4 5

▸echo 6 7 8 9 10

▸...

Полезно когда команда принимает ограниченное число аргументов или ты хочешь видеть прогресс по группам.

Типичные паттерны

Удалить старые логи

bash
find /var/log -name '*.log.gz' -mtime +30 -delete
# или
find /var/log -name '*.log.gz' -mtime +30 -print0 | xargs -0 rm

-delete встроен в find и быстрее xargs (не форкает rm). Используй его когда нужно просто удалить.

Поиск содержимого только в свежих файлах

bash
find /var/log -name '*.log' -mtime -1 -print0 | xargs -0 grep -l 'ERROR'

grep -l - печатать только имена файлов где найдено.

Перенумеровать или ssh-команда на список хостов

bash
cat servers.txt | xargs -I {} -P 10 ssh {} 'systemctl restart myapp'

▸10 параллельных SSH-сессий

Убить процессы по паттерну

bash
ps aux | grep '[m]yapp' | awk '{print $2}' | xargs kill

Без [m] сам grep попадёт в свой grep - старый трюк. Лучше: pkill myapp - атомарно и не форкает 4 процесса.

Изменить mode/owner на множестве файлов

bash
find /srv/upload -type f -print0 | xargs -0 chmod 644
find /srv/upload -type d -print0 | xargs -0 chmod 755

Конвертировать кучу картинок (параллельно)

bash
find . -name '*.png' -print0 | xargs -0 -n1 -P$(nproc) -I IMG \
    convert IMG -resize 50% IMG.thumb.png

$(nproc) - число ядер; -n1 -I обязательно если нужен placeholder.

Подвохи и ошибки

  1. xargs без -r запустит команду даже на пустом вводе:

    bash
    find . -name '*.tmp' | xargs rm        # если find ничего не нашёл - rm запустится без аргументов

    На GNU xargs (Linux) это окей (rm без аргументов скажет «missing operand»), но на BSD/macOS поведение может быть другим. Безопасно: xargs -r rm. Или используй find -exec ... + - он не запускает команду на пустом вводе.

  2. Кавычки и xargs: ' " для xargs специальные. Без -0 строки "foo bar" парсятся как одна. Это не то что хочешь когда читаешь файл с путями. Всегда -0 или -d $'\n'.

  3. find -exec ... vs find ... | xargs: для одной команды разницы по результату нет, но find -exec короче и всегда null-safe (find сам передаёт пути argv'ом). Используй его если не нужен pipe-входной поток.

  4. {} в shell-конструкции: так не работает:

    bash
    find . -name '*.log' -exec sh -c 'echo Processing {}' \;

    ▸Processing {} (без подстановки!)

    В shell-команде нужно явно передать через позиционный аргумент:

    bash
    find . -name '*.log' -exec sh -c 'echo "Processing $1"' _ {} \;

    _ - фиктивный $0, {} идёт как $1.

Когда не использовать xargs

  • Сложная shell-логика на каждый файл - пиши обычный while read-цикл (см. bash-scripting):

    bash
    find . -type f -name '*.log' -print0 | while IFS= read -r -d '' f; do
        if [[ -s "$f" ]]; then
            echo "Non-empty: $f"
        fi
    done
  • Нужны массивы / map'ы - переписывай на Python.

  • Параллельность с обработкой результатов - лучше GNU parallel (надмножество xargs с UI прогресса и логов).

§ команды

bash
find /var/log -name '*.log' -mtime +7 -exec gzip {} +

Сжать все .log старше 7 дней. + вместо \; - батчем, на порядок быстрее

bash
find . -type f -print0 | xargs -0 grep -l 'ERROR'

Безопасный grep по всем файлам - null-разделитель не ломается на пробелах

bash
cat hosts.txt | xargs -I {} -P 10 ssh {} 'uptime'

Параллельный SSH на список хостов - 10 одновременных сессий

bash
find . -name '*.tmp' -delete

Встроенное удаление в find - быстрее xargs rm и атомарно

bash
ls *.png | xargs -n1 -P$(nproc) -I IMG convert IMG -resize 50% IMG.small.png

Параллельный resize по числу ядер - заполнить все CPU работой

§ см. также

  • cmd-findfind - поиск файлов по предикатам`find` обходит дерево каталогов и применяет предикаты (имя, тип, время, размер, права). Действия: `-print` (по умолчанию), `-delete`, `-exec`, `| xargs`.
  • cmd-grepgrep - поиск строк по шаблону`grep` ищет строки по regex'у в stdin или файлах. Главные режимы: `-E` (ERE), `-P` (PCRE), `-F` (фиксированная строка), `-r` (рекурсивно по дереву).
  • cmd-sedsed - потоковый редактор текстаsed - потоковый редактор: каждой строке применяет команды (`s/a/b/`, `d`, `p`, ...). `-i` правит файл на месте; `-E` включает ERE; адресный диапазон `/start/,/end/` фильтрует блок. Hold space - вторая память.
  • cmd-awkawk - обработка структурированного текста по полямawk бьёт строку на поля по FS (default - whitespace) и применяет pattern { action }. `$1..$NF`, `NR` (счётчик), BEGIN/END для пролога и итогов. Покрывает 80% задач "обработать колонки" без Python.
  • bash-scriptingbash-скрипты - основы и идиомыBash-скрипт - текстовый файл с shebang `#!/usr/bin/env bash` и `chmod +x`. Обязательный starting point - `set -euo pipefail` и `shellcheck` для проверки.
  • cmd-rsyncrsync - инкрементальная синхронизация файловrsync копирует только изменённые блоки файлов локально или по SSH. `-avz` базовая комбинация (archive + verbose + compress). `--delete` зеркалирует. `--dry-run` обязателен перед первым запуском.

§ упоминается в уроках

  • ›intermediate-10-xargs-and-parallel
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки