Чому пошук -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) знайти. -тип f -iname ' .cpp' -exec mv {} ./test/ \; (2) знайти. -типу е -iname ' .cpp' -exec мв {} ./test/ \ +
Shahadat Хоссейн

Відповіді:


185

Сторінка керівництва (або онлайн-інструкція GNU ) майже все пояснює.

знайти команду -exec {} \;

Для кожного результату command {}виконується. Усі випадки виникнення {}замінюються назвою файлу. ;попередньо встановлено косою рисою, щоб запобігти інтерпретації оболонки.

знайти команду -exec {} +

Кожен результат додається commandта виконується згодом. Враховуючи обмеження довжини команди, я думаю, що ця команда може виконуватися більше разів, при цьому сторінка вручну підтримує мене:

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

Зверніть увагу на цю цитату зі сторінки керівництва:

Командний рядок побудований приблизно так само, як xargs будує свої командні рядки

Ось чому жодні символи заборонені між пробілами {}та +за винятком них. +заставляє виявляти, що аргументи слід додавати до команди точно так само xargs.

Рішення

На щастя, реалізація GNU mvможе сприйняти цільовий каталог як аргумент з одним -tабо більш довгим параметром --target. Це використання буде:

mv -t target file1 file2 ...

Ваша findкоманда стає:

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

З сторінки керівництва:

-exec команда;

Виконати команду; вірно, якщо статус 0 повернуто. Усі наступні аргументи для пошуку вважаються аргументами команди, поки аргумент не складається з `; ' зустрічається. Рядок `{} замінюється поточним ім'ям файлу, який обробляється скрізь, де він зустрічається в аргументах команди, а не лише в аргументах, де він один, як у деяких версіях знаходження. Обидві ці конструкції, можливо, знадобиться уникнути (із символом `\ ') або цитувати їх, щоб захистити їх від розширення оболонкою. Див. Розділ ПРИКЛАДИ для прикладів використання параметра -exec. Зазначена команда виконується один раз для кожного відповідного файлу. Команда виконується в початковому каталозі. Існують неминучі проблеми безпеки щодо використання дії -exec; ви повинні використовувати замість цього -execdir.

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

Цей варіант дії -exec виконує вказану команду на вибраних файлах, але командний рядок будується додаванням кожного вибраного імені файлу в кінці; загальна кількість викликів команди буде значно меншою, ніж кількість відповідних файлів. Командний рядок побудований приблизно так само, як xargs будує свої командні рядки. У команді дозволений лише один екземпляр `{}. Команда виконується в початковому каталозі.


1
Я знаю насправді, як це працює, я кілька разів переглядав цей посібник, але мені надійшло повідомлення про помилку використання {} +, хоча працює для {} \; і я використовую Cygwin у Windows.
Шахадат Хоссейн

1
@Shahadat: Ви читали частину перед "Зі сторінки керівництва"? Ви ставите ./test/між {}і +, але жодні символи, що не містять пробілів, між ними не допускаються.
Лекенштейн

ru кажучи, що я не повинен ставити ./test/ між {} і +. Тоді як буде працювати команда mv; mv потребує джерела, яке дорівнює {} та потребує призначення, яке є ./test/ та завершення з +. чи можете ви, будь ласка, написати команду, що вважаєте правильно?
Шахадат Хоссейн

@Shahadat: Я бачу, чого ти намагаєшся досягти. Windows повільно виконує програми, тому ви хочете об'єднати його в одну команду. Я додам альтернативу у відповідь.
Лекенштейн

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команди.


{}потрібно цитувати в оболонках fishта rc, але не в них zsh, bashані будь-яких інших оболонках сімейства Борн чи Цш.
Стефан Хазелас

@StephaneChazelas Yep, повторно перевірений під 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/Музика/ {} \+

Будь ласка, додайте пояснення до своєї відповіді, щоб інші могли дізнатися з неї
Ніко Хааз,

Вам потрібно відповісти на запитання, яке просило пояснення. Тільки код - це не відповідь.
Лайош Арпад

-1

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

Проблема полягає в find . -type f -iname '*.cpp' -exec mv {} ./test/ \+тому, що find . -type f -iname '*.cpp' -exec mv {} ./test/ + не потрібно уникати цього рішення або припиняти його+

xargs, які я давно не використовую, але я думаю, що працює як +.


Я також спробував це, але отримав те саме повідомлення про помилку. Більше того, скрізь, де я виявив, що я використовую лише +, але в моєму цигуні потрібно працювати \ + або "+" для роботи.
Шахадат Хоссейн

о, це середовище cygwin. Вибачте, тоді я не знаю, я не використовую оболонку cygwin, я просто використовую * nix.
Майк Рамірес

1
@Shahadat Hossain спробуйте -name "*.cpp"я навряд чи використовую -iname, якщо я не хочу зробити складний пошук по регулярному вираженню, наприклад -iname '??? робота'. * \. Cpp '
Майк Рамірес

1
@Mike: Я думаю, ви неправильно розумієте різницю між -inameі -name. -inameє нечутливою до регістру версією -nameта не має відмінностей в обробці регулярними виразами. Я пропоную спробувати команди перед публікацією, ваша команда також не в моїй оболонці.
Лекенштейн

1
@Lekensteyn Вже було встановлено, що так було до вашого коментаря. Я подумав, що я визнав Шахадат перед вашою посадою, це було просто "гаразд". Ні, я не запускав це вручну, я робив це зверху голови і рідко використовую таку форму регулярного вибору з пошуку. Це була лише річ типу "може допомогти".
Майк Рамірес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.