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

98

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

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

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

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

file file1.txt
file file2.txt

Но последние 2 находят с помощью -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) найти. -type f -iname ' .cpp' -exec mv {} ./test/ \; (2) найти. -type f -iname ' .cpp' -exec mv {} ./test/ \ +
Shahadat Hossain

Ответы:

185

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

find -exec command {} \;

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

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

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

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

Обратите внимание на эту цитату из справочной страницы:

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

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

Решение

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

mv -t target file1 file2 ...

Ваша findкоманда становится:

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

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

-exec команда;

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

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

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

Lekensteyn
источник
1
На самом деле я знаю, как это работает, я просматривал это руководство несколько раз, но у меня появилось сообщение об ошибке при использовании {} +, хотя работает для {} \; и я использую Cygwin в Windows.
Шахадат Хоссейн,
1
@Shahadat: вы читали часть перед "Со страницы руководства"? Вы поставили ./test/между {}и +, но между ними нельзя использовать символы, отличные от пробелов.
Lekensteyn
ru говорит, что я не должен ставить ./test/ между {} и +. Тогда как будет работать команда mv; mv нужен источник, равный {}, и пункт назначения - ./test/, и завершение с помощью +. не могли бы вы написать команду, которую считаете правильной?
Шахадат Хоссейн,
@Shahadat: Я вижу, чего вы пытаетесь достичь. Windows медленно выполняет программы, поэтому вы хотите объединить их в одну команду. Добавлю альтернативу ответу.
Lekensteyn
1
Команда +AFAIU немного странная, так как она вставляет файлы «в конец» (а не вместо {}), так зачем {}вообще использовать - это сбивает с толку. Спасибо за -tвариант, о котором я не знал, похоже, этот вариант был создан как обходной путь к этой -exec +проблеме!
e2-e4
6

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

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

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

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

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

Arvymetal
источник
{}потребности быть указаны в fishи rcоболочки, но не zsh, bashни каких - либо других оболочек Bourne или CSH семей.
Стефан
@StephaneChazelas Да, повторно протестировано под Ubuntu с bash, действительно, кавычки не требуются. Любопытно, что у меня была проблема, если бы я не цитировал их под MacOS (используя zsh). Но у меня нет Mac, чтобы попробовать еще раз ...
arvymetal
3

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

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

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

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

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

Стефан Хазелас
источник
+1 ... операция в оболочке в качестве предварительной подготовки к выполнению команды на самом деле полезна для множества случаев использования ... приятно.
Cbhihe
0
find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+
DarkLabs
источник
Пожалуйста, добавьте пояснение к своему ответу, чтобы другие могли извлечь из него уроки
Нико Хаасе,
Вам необходимо ответить на вопрос, на который просили пояснения. Только код - это не ответ.
Лайош Арпад,
-1

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

Проблема в том, find . -type f -iname '*.cpp' -exec mv {} ./test/ \+что find . -type f -iname '*.cpp' -exec mv {} ./test/ + не нужно выходить из нее или прекращать+

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

Майк Рамирес
источник
Я тоже пробовал с этим, но получил то же сообщение об ошибке. Более того, везде я использовал только +, но в моем cygwin я должен использовать \ + или «+» для работы.
Шахадат Хосейн, 09
о, это среда cygwin. Извините, тогда я не знаю, я не использую оболочку cygwin, я просто использую * nix.
Майк Рамирес,
1
@Shahadat Hossain попробуйте -name "*.cpp"Я почти не использую -iname, если я не хочу выполнять какой-то сложный поиск по регулярным выражениям, например -iname '??? work. * \. Cpp'
Майк Рамирес
1
@Mike: Я думаю, вы неправильно понимаете разницу между -inameи -name. -inameявляется версией без учета регистра -nameи не имеет различий в обработке регулярных выражений. Я предлагаю попробовать команды перед публикацией, ваша команда также не работает в моей оболочке.
Lekensteyn
1
@Lekensteyn Это уже было установлено до вашего комментария. Я думал, что признал Шахадат до вашего поста, это было простое «хорошо». Нет, я не запускал его вручную, я делал это из головы и редко использую эту форму поиска по регулярным выражениям с помощью find. Это было просто «могло бы помочь».
Майк Рамирес,