find -exec + vs find | xargs: какой выбрать?

32

Я понимаю, что -execможет выбрать +вариант для имитации поведения xargs. Есть ли ситуации, когда вы предпочитаете одну форму другой?

Лично я предпочитаю первую форму, если только не использую трубу. Я уверен, что разработчики findдолжны были сделать соответствующие оптимизации. Я прав?

rahmu
источник

Ответы:

21

Возможно, вы захотите связать вызовы, чтобы найти (однажды, когда вы узнали, что это возможно, что может быть сегодня). Это, конечно, возможно только до тех пор, пока вы остаетесь в поиске. Как только вы отправляете xargs по конвейеру, это выходит за рамки.

Небольшой пример, два файла a.lst и b.lst:

cat a.lst
fuddel.sh
fiddel.sh

cat b.lst
fuddel.sh

Здесь нет хитрости - просто тот факт, что оба содержат «fuddel», но только один содержит «fiddel».

Предположим, мы этого не знали. Мы ищем файл, который соответствует 2 условиям:

find -exec grep -q fuddel {} ";" -exec grep -q fiddel {} ";" -ls
192097    4 -rw-r--r--   1 stefan   stefan         20 Jun 27 17:05 ./a.lst

Ну, может быть, вы знаете синтаксис для grep или другой программы для передачи обеих строк как условия, но это не главное. Здесь можно использовать любую программу, которая может возвращать истину или ложь, учитывая файл в качестве аргумента - grep был просто популярным примером.

И обратите внимание, вы можете использовать команду find -exec с другими командами поиска, такими как -ls или -delete или что-то подобное. Обратите внимание, что delete не только делает rm (удаляет файлы), но и rmdir (удаляет каталоги).

Такая цепочка читается как комбинация команд AND, если не указано иное (а именно с -orпереключателем (и паренсом (которые требуют маскировки))).

Таким образом, вы не покидаете цепочку поиска, что очень удобно. Я не вижу никакого преимущества в использовании -xargs, так как вы должны быть осторожны при передаче файлов, а это то, что не нужно делать при поиске - он автоматически обрабатывает передачу каждого файла в качестве единственного аргумента для вас.

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

неизвестный пользователь
источник
3
Этот пост открыл мне глаза на новый способ использования find. Большое спасибо!
Рахму
1
«Я не вижу никакого преимущества в использовании -xargs». Как это -execсделать, xargs -P4чтобы три из четырех ядер не оставались без дела?
Дамиан Йеррик
1
@DamianYerrick: завершить команду -exec не в «;» но со знаком + (/ плюс).
пользователь неизвестен
25

Для безопасной передачи имен файлов xargsтребуется, чтобы ваша опция findподдерживала, -print0а у вас xargsбыла соответствующая опция для ее чтения ( --nullили -0). В противном случае имена файлов с непечатаемыми символами, обратной косой чертой, кавычками или пробелами в имени могут вызвать непредвиденное поведение. С другой стороны, он find -exec {} +входит в спецификацию POSIXfind , поэтому он переносим, ​​и он примерно так же безопасен, как find -print0 | xargs -0и определенно безопаснее, чем find | xargs. Я рекомендовал бы никогда не делать find | xargsбез -print0.

jw013
источник
6
Заметным исключением из переносимости find … -exec … {} +является OpenBSD, которая приобрела эту функцию только с версией 5.1, выпущенной в 2012 году. Все BSD работали в -print0течение нескольких лет, даже OpenBSD (хотя она тоже некоторое время сопротивлялась этой функции). Солярис, с другой стороны, придерживается функций POSIX, так что вы получите, -exec +и нет -print0.
Жиль "ТАК ... перестать быть злым"
-print0это боль, и хотя вы можете утверждать, что xargs --delimiter "\n"это не эквивалентно, я никогда не использовал первое после обнаружения последнего.
Шридхар Сарнобат
3
Я не понимаю, как -0это больше боли, чем --delimiter "\n".
jw013,
2
В дополнение к этому -0, GNU xargsнеобходимо -rизбегать запуска команды, если нет ввода.
Стефан Шазелас
2
Другая проблема | xargs -r0 cmdзаключается в том, что cmdэто влияет на stdin (в зависимости от xargsреализации, это /dev/nullили канал.
Стефан Chazelas
10

Если вы используете -exec ... ;форму (не обращая внимания на точку с запятой), вы запускаете команду один раз для каждого имени файла. Если вы используете -print0 | xargs -0, вы запускаете несколько команд для имени файла. Вы обязательно должны использовать -exec +форму, которая помещает несколько файлов в одну командную строку и намного быстрее, когда задействовано большое количество файлов.

Большим плюсом использования xargsявляется возможность запускать несколько команд параллельно xargs -P. В многоядерных системах это может значительно сэкономить время.

Алексиос
источник
6
Вы имели в виду -Pвместо -p. Имейте в виду xargs -P, не в стандарте POSIX, а find -exec {} +есть, что важно, если вы собираетесь на переносимость.
jw013
@Alexios Вам не нужно экранировать знак плюс, потому что он не имеет специального значения для оболочки: find /tmp/ -exec ls "{}" +работает просто отлично.
Даниэль Куллманн
1
Коррент, конечно. Я так долго убегал от всего -exec(я мазохист, я даже не использую кавычки {}, я всегда печатаю \{\}, не спрашиваю), все выглядит так, как будто его сейчас нужно экранировать.
Алексиос
1
@Alexios: Если вы найдете пример (за исключением того, что мазохист), где маскирование фигурных скобок полезно, пожалуйста, предоставьте его в качестве ответа на мой вопрос здесь - на самом деле этот совет устарел и является лишь реликтом на странице руководства. Я никогда не видел пример, где find /tmp/ -exec ls {} +бы не работал.
пользователь неизвестен
1
Для меня это мышечная память, сформировавшаяся во времена SunOS 4. Так как я занимался этим годами, я совершенно не заметил, когда bashначал дословно принимать скобки. Я почти уверен, что по крайней мере один из старых снарядов, которые я использовал, бросил шипучие приступы, если скобки не сбежали.
Алексиос
8

Что касается производительности, я подумал, что -exec … +было бы лучше, потому что это единственный инструмент, выполняющий всю работу, но часть документации GNU findutil говорит, что -exec … +в некоторых случаях это может быть менее эффективно:

[найти с помощью -exec … +] может быть менее эффективным, чем некоторые виды использования xargs; например, xargsпозволяет создавать новые командные строки во время выполнения предыдущей команды и позволяет указать количество команд для параллельного выполнения. Однако find ... -exec ... +конструкция обладает преимуществом широкой портативности. GNU findutils не поддерживал ' -exec ... +' до версии 4.2.12 [январь 2005] ; Одна из причин этого заключается в том, что -print0в любом случае у него уже было действие « ».

Я не был точно уверен, что это значит, поэтому я спросил в чате, где Дероберт объяснил это так:

findвероятно, может продолжить поиск следующей партии файлов во время -exec … +работы, но это не так.
find … | xargs …делает, потому что тогда поиск - это другой процесс, и он продолжает работать, пока буфер канала не заполнится

(Форматирование мной.)

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

Здесь, на этом сайте, я думаю, что лучше советовать людям использовать -exec … +форму всегда, когда это возможно, потому что это проще и по причинам, указанным в других ответах здесь (например, обработка странных имен файлов без необходимости много думать).

PHK
источник