Почему find -exec mv {}./target/+ не работает? (на cygwin)

Я хочу точно знать, что делать {} \; и {} \+ и | xargs .... Просьба пояснить их пояснениями.

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

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

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

file file1.txt
file file2.txt

Но последние 2 find с командами -exec запускают команду файла один раз для всех файлов, как показано ниже:

file file1.txt file2.txt

Затем я запускаю следующие команды, на которых первый запускается без проблем, а второй - с сообщением об ошибке.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

Для команды с {} \+ она дает мне сообщение об ошибке

find: missing argument to `-exec'

почему? может ли кто-нибудь объяснить, что я делаю неправильно?

Ответ 1

справочная страница (или онлайн-руководство по GNU) в значительной степени объясняет все.

find -exec command {} \;

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

find -exec command {} +

Каждый результат добавляется к command и выполняется впоследствии. Принимая во внимание ограничения длины команды, я полагаю, что эта команда может выполняться больше раз, при этом страница справочной страницы меня поддерживает:

общее количество вызовов команды будет намного меньше количества согласованных файлов.

Обратите внимание на эту цитату на странице руководства:

Командная строка построена почти так же, как xargs создает свои командные строки

То почему никакие символы не допускаются между {} и + за исключением пробелов. + позволяет find обнаружить, что аргументы должны быть добавлены к команде точно так же, как xargs.

Решение

К счастью, реализация GNU mv может принимать целевой каталог в качестве аргумента, либо -t, либо более длинный параметр --target. Это будет:

mv -t target file1 file2 ...

Ваша команда find будет выглядеть следующим образом:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

На странице руководства:

-exec команда;

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

-exec command {} +

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

Ответ 2

Я столкнулся с той же проблемой в Mac OSX, используя оболочку ZSH: в этом случае для mv нет опции -t, поэтому мне пришлось найти другое решение. Однако следующая команда выполнена успешно:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;
Секрет заключался в том, чтобы процитировать фигурные скобки. Нет необходимости, чтобы фигурные скобки находились в конце команды exec .

Я тестировал Ubuntu 14.04 (с оболочками BASH и ZSH), он работает одинаково.

Однако при использовании знака + кажется, что он должен быть в конце команды exec .

Ответ 3

Стандартным эквивалентом реализаций find -iname ... -exec mv -t dest {} + для find, которые не поддерживают реализации -iname или mv, которые не поддерживают -t, является использование оболочки для переопределения аргументов:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "[email protected]" /dest/dir/' sh {} +

Используя -name '*.[cC][pP][pP]', мы также избегаем зависимости от текущей локали, чтобы решить, что такое верхняя версия c или p.

Обратите внимание, что +, в отличие от ;, не является специальным в любой оболочке, поэтому его не нужно указывать (хотя цитирование не повредит, за исключением, конечно, оболочек типа rc, которые не поддерживают \ как оператор цитирования).

Конечная / в /dest/dir/ заключается в том, что mv завершается с ошибкой вместо переименования foo.cpp в /dest/dir в том случае, когда был найден только один файл cpp и /dest/dir didn ' t существует или не является каталогом (или символической ссылкой на каталог).

Ответ 4

нет, разница между + и \; должна быть отменена. + добавляет файлы в конец команды exec, затем запускает команду exec и \; запускает команду для каждого файла.

Задача find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ должна быть find . -type f -iname '*.cpp' -exec mv {} ./test/ + не нужно ее удалять или завершать +

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