Как в 20 выглядеть на 40?

Правильно! Установить Linux!

Я всегда обходил стороной команду find, наверное потому что я никогда не умел ее готовить. Мне всегда было проще находить нужное в midnight commander.

У меня кстати даже постов про find не было в этом канале.

Но для bash скриптов mc это не панацея, поэтому изредка приходилось гуглить про find и что-то лепить.

Сегодня затрём как раз про find. Но не за Базу, а за его скорость и бест-практики.

С помощью find мы ищем по разным паттернам и условиям. Порой необходимо совершить какие-то действия с найденными результатами. И find не может нас этим порадовать. Приходится передавать данные внешним утилитам.

У find есть пару опций:

  • exec = запускает утилиту и передает в неё аргументы
  • execdir = перед запуском утилиты, переходит в каталог с найденным

Синтаксис этих штук такой:

-exec команда {} суффикс
  • команда = утилита, которую запускаем
  • {} = развернется в найденные файлы
  • ; = суффикс, на каждый файл запускается команда
  • + = суффикс, команда запускается с группой файлов

Символ ; обязательно нужно экранировать либо поместить в кавычки.

Хорош с теорией, погнали в практику:

find ~/ -type f -exec sh -c 'echo $$' {} \;

Эта команда будет искать обычные файлы в домашнем каталоге пользователя. Утилитой для обработки, у нас будет интерпретатор, который выведет на экран идентификатор процесса. Интерпретатор используем чисто в лабораторных целях.

По итогу на экран я получил 100500 строчек такого вида:

26719
26720
26721

Это список процессов, которые были задействованы в результате поиска. Чёто прям дохуя. Если надоело ждать результата, жми CTRL+C.

А теперь давай оптимизируем!

find ~/ -type f -exec sh -c 'echo $$' {} +

По итогу я получаю одну цифру 27413, один идентификатор процесса, в отличие от предыдущего варианта. Круто? Да охуеть не встать!

Но что произошло? Ща объясню. В оптимизированном варианте, имена файлов разбиваются на максимально большие группы и затем передаются в утилиту. Вот в этом и есть разница.

Ведь, чтобы запустить внешнюю утилиту, нужно совершить кучу лишний телодвижений. Если запускать на каждый найденный файл отдельный процесс, то тратится куча ресурсов, причем зря. Естественно это прилично влияет на скорость.

Роман Шубин
Роман Шубин
CEO & CTO, Главред в «Цифровой улей»
Задать вопрос
Проще ведь сразу скормить утилите группу найденных файлов и никому не ебать мозги.

Стопэ. Это еще не всё, можно еще больше разогнать

Включаем режим оверклоакинга!

Будем использовать утилиту xargs, она как раз входит в пакет с find. Утилита xargs запускает процессы параллельно. Уже понимаешь чо щас будет?

find ~/ -type f -print0 | xargs -0 -n4 -P2 команда

Xargs читает стандартный ввод и разбивает его на группы аргументов. А после этого запускает команду с данной группой аргументов.

По ключам:

  • 0 = аргументы разделяются символами NUL ASCII. А спецсимволы косой черты \ и кавычками, воспринимаются как обычные.
  • n = максимально количество аргументов переданных за один раз
  • P = максимально количество процессов запущенных одновременно. Если указать 0, запустится как можно больше таких процессов.
  • команда = внешняя команда которая будет запущена. Если ничего не указывать, на экран выведутся группы аргументов, удобная фича для отладки.

А еще если во время работы xargs, ей послать сигнал SIGUSR1 (kill), то количество одновременно запускаемых процессов будет увеличено. А для SIGUSR2, будет уменьшено.

SIGUSR1 и SIGUSR2 могут быть использованы программой для произвольных нужд, например для межпроцессной (или межпоточной) синхронизации, управления фоновыми процессами (демонами) и т. п. По умолчанию, сигналы SIGUSR1 и SIGUSR2 завершают выполнение процесса.

Это базовая команда для правильной работы find + xargs:

find -print0 | xargs -0 command

Но учти, если command у тебя будет оболочкой sh/bash, тогда нужно правильно обработать аргументы и учесть всякие splitting, globbing и т.п.

Ладно чо. Всем хорошего отдыха и заебатых шашлычков. На 01.05 я возьму отгул, а там дальше уже всё по плану: за кишочки подергаем, да на страусе (strace) покатаемся. Будет охуенно!