Использование точки с запятой (;) против плюс (+) с exec в find

159

Почему разница в использовании

find . -exec ls '{}' \+

и

find . -exec ls '{}' \;

Я получил:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1
Анкур Агарвал
источник

Ответы:

249

Это может быть лучше всего проиллюстрировано на примере. Допустим, что findвключает эти файлы:

file1
file2
file3

Использование -execс точкой с запятой ( find . -exec ls '{}' \;), выполнит

ls file1
ls file2
ls file3

Но если вы используете вместо этого знак плюс ( find . -exec ls '{}' \+), столько имен файлов, сколько возможно, передаются в качестве аргументов одной команде:

ls file1 file2 file3

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

Мартин
источник
1
Спасибо. это очень полезно для сортировки результирующих файлов: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas
1
Тупой вопрос: я замечаю, что с +ассоциированным -execвсегда экранируется, но с +ассоциированным -mtime- нет. Вы знаете причину? Я думаю, это привычка избегать ;связанных с -exec.
Кевинарпе
3
@kevinarpe действительно, я бы записал это по привычке ;. Не могу себе представить, что когда-либо нужно бежать+
Мартин
36

Все ответы пока верны. Я предлагаю это как более ясную (для меня) демонстрацию поведения, которое описано с использованием, echoа не ls:

С точкой с запятой, команда echo вызывается один раз для каждого найденного файла (или другого объекта файловой системы):

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

С плюсом, команда echo вызывается только один раз. Каждый найденный файл передается в качестве аргумента.

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

Если findпоявляется большое количество результатов, вы можете обнаружить, что вызываемая команда ограничивает количество аргументов.

Johnsyweb
источник
1
Не должны ли найти добавить результаты только к числу, которое позволяет безопасно передать его в оболочку? По крайней мере, это то, что xargsделают ... в принципе это никогда не захлебывается слишком много аргументов.
Rmano
1
@Rmano: я видел findxargs) в Solaris испускает больше аргументов, чем можно было бы использовать. xargsfind) в findutils` ГНУ , кажется, ведут себя более разумно, но не каждый использует GNU.
Johnsyweb
2
@Johnsyweb, все POSIX findбудут пытаться избежать ограничения количества аргументов. И это включает в себя Солярис (по крайней мере 10). Где это может потерпеть неудачу, так это если вы делаете что-то вроде find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +или find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +, но на findсамом деле не можете быть обвинены в этом. Обратите внимание, что GNU findбыла одной из последних findподдерживаемых реализаций +(раньше было неудобно переносить скрипт на системы GNU).
Стефан Шазелас,
19

От man find:

-exec команда;

Выполнить команду; true, если возвращается 0 статус. Все последующие аргументы для поиска принимаются в качестве аргументов команды до аргумента, состоящего из ';' встречается. Строка '{}' заменяется текущим именем файла, которое обрабатывается везде, где оно встречается в аргументах команды, а не только в аргументах, где он один, как в некоторых версиях find. Обе эти конструкции, возможно, должны быть экранированы (с '\') или заключены в кавычки, чтобы защитить их от расширения оболочкой. Смотрите секцию EXAMPLES sec для примеров использования опции '-exec'. Указанная команда запускается один раз для каждого соответствующего файла. Команда выполняется в начальном каталоге. Существуют неизбежные проблемы безопасности, связанные с использованием параметра -exec;

-exec команда {} +

Этот вариант опции -exec запускает указанную команду для выбранных файлов, но командная строка создается путем добавления каждого выбранного имени файла в конце ; общее количество вызовов команды будет намного меньше, чем количество совпавших файлов. Командная строка строится во многом так же, как xargs создает свои командные строки. В команде допускается только один экземпляр «{}». Команда выполняется в начальном каталоге.

Итак, насколько я понимаю, \;выполняет отдельную команду для каждого найденного файла find, в то \+время как добавляет файлы и выполняет одну команду для всех из них. Это \побег, так что это:

ls testdir1; ls testdir2 

против

ls testdir1 testdir2

Выполнение вышеизложенного в моей оболочке отражало вывод вашего вопроса.

пример того, когда вы хотели бы использовать \+

Предположим, два файла, 1.tmpи 2.tmp:

1.tmp:

1
2
3

2.tmp:

0
2
3

С участием \; :

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

Принимая во внимание, что если вы используете \+(для объединения результатовfind ):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

Так что в этом случае это разница между diff 1.tmp; diff 2.tmpиdiff 1.tmp 2.tmp

Есть случаи, когда \;это уместно и \+будет необходимо. Использование \+with rmявляется одним из таких примеров, когда при удалении большого количества файлов производительность (скорость) будет выше \;.

matchew
источник
Я тоже могу прочитать справочную страницу. И я сделал, но я не думаю, что я понимаю разницу между использованием; против +
Анкур Агарвал
Я не думаю, что -1 был справедливым, я объяснил свое понимание этого человека. Я не просто скопировал человека и ушел. но я отредактировал свой ответ, чтобы включить лучший пример.
Матч
10

findимеет специальный синтаксис. Вы используете их {}как они есть, потому что они имеют смысл найти в качестве пути к найденному файлу, и (большинство) оболочки не интерпретируют их иначе. Вам нужна обратная косая черта, \;потому что точка с запятой имеет значение для оболочки, которая съедает ее прежде, чем findсможет ее получить. Итак, что findхочет видеть ПОСЛЕ того, как оболочка готова, в списке аргументов, передаваемом программе C, есть

"-exec", "rm", "{}", ";"

но тебе нужно \; в командной строке получить точку с запятой через оболочку для аргументов.

Вы можете сойти с рук, \{\}потому что интерпретируемая оболочкой интерпретация \{\}просто{} . Точно так же вы можете использовать '{}'.

То, что вы не можете сделать, это использовать

 -exec 'rm {} ;'

потому что оболочка интерпретирует это как один аргумент,

"-exec", "rm {};"

а rm {} ;не название команды. (По крайней мере, если кто-то действительно не крутится.)

Обновить

разница между

$ ls file1
$ ls file2

и

$ ls file1 file2

Это +объединяет имена в командной строке.

Чарли Мартин
источник
1
Я понимаю, что вы говорите. Я спрашиваю, в чем разница между использованием; vs +
Анкур Агарвал
1
извините, но вы внимательно прочитали мой вопрос или мой комментарий? Может быть, мне нужно перефразировать это. Почему при вводе точки с запятой с exec в find используется другое значение o / p, а при использовании плюс с exec в find?
Анкур Агарвал
2
Это отличное объяснение ПОЧЕМУ команда такая, что принятый ответ не распространяется. Спасибо!
Шервин Ю
1

Разница между ;(точка с запятой) или +(знак плюс) заключается в том, как аргументы передаются в параметр find -exec/ -execdir. Например:

  • Использование ;будет выполнять несколько команд (отдельно для каждого аргумента),

    Пример:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    Все последующие аргументы find принимаются в качестве аргументов команды.

    Строка {}заменяется текущим именем обрабатываемого файла.

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

    Пример:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    Командная строка строится путем добавления каждого выбранного имени файла в конце.

    В {}команде допускается только один экземпляр .

Смотрите также:

kenorb
источник
-5

мы пытались найти файл для домашнего хозяйства.

найти . -exec echo {} \; Команда побежала за ночь, в итоге безрезультатно.

найти . -exec echo {} \ + имеют результаты и заняли всего несколько часов.

Надеюсь это поможет.

Рон
источник
2
Этот ответ не объясняет, как эти два способа работают и как отличаются результаты, полученные ими.
misko321