Когда найдешь. -exec COMMAND {} + `выполнить COMMAND несколько раз?

9

Если я сделаю

find . -exec echo {} +

он печатает все пути в одну строку, т.е. команда echoвыполняется только один раз.

Но согласно man find,

-exec command {} +
    ... the number of invocations of the command will 
be much  less  than  the  number  of matched files. ...

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

замороженное пламя
источник

Ответы:

8

POSIX определил find -exec utility_name [аргумент ...] {} + как:

Конец основного выражения должен быть отмечен <точкой с запятой> или знаком «плюс». Только знак «плюс», который следует сразу за аргументом, содержащим только два символа «{}», должен быть акцентирован на конце основного выражения. Другие варианты использования знака «плюс» не должны рассматриваться как особые. Если первичное выражение акцентировано <точка с запятой>, утилита utility_name должна вызываться один раз для каждого пути и первичное значение должно оцениваться как true, если утилита возвращает нулевое значение в качестве состояния выхода. Utility_name или аргумент , содержащий только два символа «{}» должен быть заменен на текущем пути. Если имя_устройства или аргументСтрока содержит два символа "{}", но не только два символа "{}", это определяется реализацией, заменяет ли find эти два символа или использует строку без изменений.

Если первичное выражение акцентировано знаком <плюс>, первичное всегда должно оцениваться как истинное, а имена путей, для которых оценивается первичное, должны быть объединены в наборы. Утилита utility_name должна вызываться один раз для каждого набора агрегированных путей. Каждый вызов должен начинаться после агрегирования последнего пути в наборе и должен завершаться до выхода утилиты поиска и до агрегирования первого пути в следующем наборе (если есть) для этого первичного, но в противном случае не указано, является ли вызов происходит до, во время или после оценки других праймериз. Если любой вызов возвращает ненулевое значение в качестве состояния выхода, поиск утилита должна возвращать ненулевой статус выхода. Аргумент, содержащий только два символа «{}», должен быть заменен набором агрегированных имен путей, причем каждое имя пути передается в качестве отдельного аргумента вызываемой утилите в том же порядке, в котором она была агрегирована. Размер любого набора из двух или более путей должен быть ограничен таким образом, чтобы выполнение утилиты не приводило к превышению системного лимита {ARG_MAX} . Если присутствует более одного аргумента, содержащего два символа «{}», поведение не определено.

Когда длина установленного вами имени файла превышает системную ARG_MAX, команда выполняется.

Вы можете получить ARG_MAXс помощью getconf :

$ getconf ARG_MAX
2097152

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

cuonglm
источник
Я провел эксперимент, используя find / -exec echo | wcи измерив соотношение между количеством символов и количеством строк, и обнаружил, что максимальная длина используемой командной строки findзначительно меньше теоретического предела POSIX и намного ближе к Size of command buffer we are actually usingстроке в выходных данных xargs --show-limits. Это верно для Linux и может быть верно для реализации Mac OS find, хотя xargsне будет печатать значение в Mac OS. Есть идеи, почему это происходит?
pqnet
--show-limitsPOSIX не указывает, реализация Mac OS xargsне поддерживает его. find / -exec echo | wcне сработает Помните, что ARG_MAXвозвращаются байты. И это максимальная длина аргументов для exec(3)функций.
Cuonglm
Я знаю, что --show-limitsэто не POSIX, хотя это не максимальная длина аргумента, используемая find, который использует меньшее значение. Я не понимаю, почему вы говорите, что find / -exec echo | wcэто не сработает: по моему мнению, это хороший способ оценить реальную стоимость (и, как я вижу, лучше, чем использовать getconf ARG_MAX). Кроме того, моя файловая система в основном, если не все символы ASCII, так что количество символов примерно равно количеству байтов.
pqnet
@pqnet: используйте find / -exec sh -c 'echo $@ | wc -c' _ {} +isntead.
Cuonglm
извините, я написал это неправильно, я фактически использовалfind / -exec echo {} + | wc -lc
pqnet
7

Существует максимальная длина списка аргументов для нового процесса в системе POSIX. findразделит выполнение, если пути к файлам длиннее, чем этот. Чтобы увидеть ограничение для Linux, используйте xargs --show-limits(не работайте в Mac OS, если кто-то знает лучшую альтернативу, пожалуйста, прокомментируйте здесь)

edit: украденный прямо из ответа Gnouc, POSIX способ получить максимальную длину списка аргументов getconf ARG_MAX. Тем не менее, я провел эксперимент на моей машине Mac OS, и, похоже, findиспользует чуть больше половины этого числа. Это согласуется с тем фактом, что в системе, где он работает, xargs --show-limitsговорит нам, что он не будет использовать максимальную длину аргумента (в этом случае он также будет использовать около половины этого числа), однако я не смог найти объяснение для этого.

редактировать 2: кажется, что единственный надежный способ определить, сколько параметров findбудет слипаться для каждого вызова, это эксперимент, например, запустив

find / -exec echo {} + | wc -cl

Так как выходные данные findимеют строку для каждого echoвызова, их можно сосчитать, используя wc -l. Общее число байтов echoed является выходом wc -cвместо. Разделив один на другой, вы получите среднее число байтов в параметрах для каждого вызова команды (хотя и немного более низкое значение из-за округления, примерно половина средней длины пути в вашей системе)

pqnet
источник
xargsне использует полную максимальную длину аргумента, потому что многие программы добавляют несколько дополнительных аргументов, а затем передают аргументы другим программам. Если xargsаргументы заполняются до абсолютного максимума, такие программы ломаются, потому что не было бы места для этих дополнительных аргументов.
HVd
@hvd имеет смысл. Но тогда, есть ли способ POSIX узнать, какая часть буфера используется xargsили find?
pqnet
Вы можете выполнить его с очень длинным списком аргументов, определяя, сколько аргументов было передано при первом вызове (что-то вроде yes . | xargs | head -n 1 | wc -c), и сравнивая это с выводом getconf ARG_MAX. Но, на самом деле, пробуя это в моей системе, я получаю настолько большую разницу, что кажется, что есть нечто большее, чем я знаю.
HVd
так что все сводится к экспериментам ... Я
обновлю