find и xargs

Материал из llmdocs — справочник разработчика
find и xargs
Типутилиты командной строки Unix
ПоявлениеPWB/Unix (1970-е)
РеализацииGNU findutils, BSD find, BusyBox
СтандартPOSIX
Безопасная работа с именами-print0 и xargs -0
Современная альтернативаfd (Rust)

find — утилита Unix для поиска файлов и каталогов по множеству признаков: имени, типу, размеру, времени изменения, владельцу, правам — с возможностью сразу выполнить действие над каждым найденным файлом. xargs читает поток строк со стандартного ввода и подставляет их как аргументы в команду. Вместе они решают задачи вроде «удалить все логи старше 30 дней» или «найти все файлы .py и проверить их линтером».

Обе утилиты появились в 1970-х в PWB/Unix и описаны в стандарте POSIX;[2][3] в Linux используется реализация GNU findutils.

find: основы

Синтаксис: find [где искать] [условия] [действия].

find . -name "*.py"                       # по шаблону имени (с учётом регистра)
find . -iname "readme*"                   # без учёта регистра
find /var/log -type f -name "*.log"       # только файлы (d — каталоги, l — ссылки)
find . -type f -size +100M                # больше 100 МБ
find . -mtime -1                          # изменённые за последние сутки
find /tmp -type f -mtime +7 -delete       # удалить файлы старше 7 дней
find . -empty -type d                     # пустые каталоги
find . -user www-data ! -perm -u=r        # владелец www-data, но без права чтения
find . -name node_modules -prune -o -name "*.js" -print   # не заходить в node_modules

Шаблон имени нужно брать в кавычки: иначе оболочка раскроет *.py в текущем каталоге до запуска find.

Условия

Условие Смысл
-name, -iname, -path имя или путь по шаблону
-regex путь по регулярному выражению
-type f/d/l файл, каталог, символическая ссылка
-size +N/-N размер больше или меньше (k, M, G)
-mtime, -mmin, -newer время изменения в днях, минутах, новее файла
-user, -group, -perm владелец, группа, права
-maxdepth N глубина обхода
!, -o, ( ) отрицание, «или», группировка (скобки экранируются)

Условия, записанные подряд, объединяются через «и».[1]

Действия

find . -name "*.tmp" -delete                    # удалить
find . -name "*.sh" -exec chmod +x {} \;        # команда для каждого файла отдельно
find . -name "*.py" -exec ruff check {} +       # одна команда для пачки файлов — быстрее
find . -type f -printf "%s %p\n" | sort -n | tail   # самые большие файлы (GNU find)

{} заменяется именем файла. Вариант \; запускает команду для каждого файла, + — передаёт сразу много файлов в одну команду, что на порядки быстрее для тысяч файлов.

xargs

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

cat urls.txt | xargs -n 1 curl -sO              # по одному аргументу на запуск
grep -rl "TODO" src/ | xargs wc -l              # число строк в файлах с TODO
find . -name "*.log" -print0 | xargs -0 gzip    # безопасно для имён с пробелами
find . -name "*.png" -print0 | xargs -0 -P 8 -n 10 optipng   # 8 процессов параллельно
echo "a b c" | xargs -I{} echo "элемент: {}"
  • -0 вместе с find -print0 разделяет имена нулевым байтом. Без этого имена с пробелами, кавычками и переводами строк разбиваются неправильно — частая причина ошибок и даже удаления не тех файлов.
  • -P N запускает до N процессов параллельно — простой способ задействовать все ядра. Для сложных случаев есть GNU Parallel.[5]
  • -I{} подставляет строку в заданное место команды, -n N ограничивает число аргументов на запуск.
  • -r (GNU) не запускает команду, если входных данных нет.

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

# найти и удалить старые резервные копии, сначала проверив список
find /backups -name "*.tar.gz" -mtime +30 -print
find /backups -name "*.tar.gz" -mtime +30 -delete

# что занимает место
find / -xdev -type f -size +1G 2>/dev/null

# поиск текста только в файлах определённого типа
find . -name "*.yaml" -print0 | xargs -0 grep -n "image:"

# переименовать расширение
find . -name "*.jpeg" -exec sh -c 'mv "$1" "${1%.jpeg}.jpg"' _ {} \;

Перед -delete или деструктивным -exec полезно сначала запустить команду с -print и посмотреть список — find выполнит действие над всем найденным без вопросов.

fd — современная альтернатива

Утилита fd, написанная на Rust, предлагает более простой синтаксис и разумные значения по умолчанию: ищет по подстроке или регулярному выражению, пропускает скрытые файлы и файлы из .gitignore, раскрашивает вывод и работает параллельно.[4]

fd "\.py$"                    # аналог find . -name "*.py", без .gitignore-файлов
fd -e log -x gzip             # найти .log и сжать каждый
fd -H config                  # включая скрытые файлы

Для поиска содержимого в файлах используют grep или ripgrep, для массовой правки текста — sed и awk.

См. также

Источники

  1. GNU Findutils manual
  2. POSIX.1-2024 — find
  3. POSIX.1-2024 — xargs
  4. fd — a simple, fast and user-friendly alternative to find
  5. GNU Parallel