find: -exec vs xargs (иначе почему «find | xargs basename» ломается?)

15370
quack quixote

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

$ find . -name '*.deb' -print | xargs basename  basename: extra operand `./pool/main/a/aalib/libaa1_1.4p5-37+b1_i386.deb' Try `basename --help' for more information. 

Я получаю то же самое (точно такую ​​же ошибку) с любым из этих вариантов:

$ find . -name '*.deb' -print0 | xargs -0 basename  $ find . -name '*.deb' -print | xargs basename {} 

Это, с другой стороны, работает как ожидалось:

$ find . -name '*.deb' -exec basename {} \; foo bar baz 

Это происходит в современных Cygwin и Debian 5.0.3. Мой диагноз состоит в том, что xargs по какой-то причине передает две строки ввода в basename, но почему? Что тут происходит?

10

4 ответа на вопрос

22
akira

Потому что basenameхочет только один параметр ... не много. И xargsсоздает много параметров.

Чтобы решить вашу реальную проблему (перечислите только имена файлов):

 find . -name '*.deb' -printf "%f\n" 

Который печатает только «базовое имя» (человек найти):

 %f File's name with any leading directories removed (only the last element). 
оооо .... * / снова хлопает себя по лбу / * я думаю, что мне нужна книга "найти для чайников" ... quack quixote 15 лет назад 1
я думал, что смысл `xargs` в том, что он создает список аргументов и передает каждый из них команде, которая идет после? иначе какая разница между этим и `найти. -имя '* .deb' | basename`? WindowsMaker 7 лет назад 0
Базовое имя GNU теперь имеет параметр `-a`:« поддерживать несколько аргументов и обрабатывать каждый как имя ». bishop 7 лет назад 0
@WindowsMaker `xargs` преобразует` stdin` в аргументы команды. В некотором смысле это противоположность `echo`, которая преобразует свои аргументы в` stdout`. Разница между `найти ... | xargs -n1 basename` или `find ... | xargs basename -a` и `find ... | basename` состоит в том, что первые два будут работать с реализациями `basename`, которые игнорируют` stdin`. 8bittree 6 лет назад 1
17
perlguy9

Попробуй это:

find . -name '*.deb' | xargs -n1 basename 
это не объяснение, это обходной путь. и обходной путь так же хорош, как простой вызов 'basename' через -exec для любого найденного файла. akira 15 лет назад 0
+1 ... хотя это и не объяснение, это привело бы меня к исследованию переключателя xargs, который вы показываете, что в конечном итоге привело бы меня к хлопающему по лбу движению, которое я просто использовал, читая ответы Акиры и Джона Т ... quack quixote 15 лет назад 4
Вот как я это делаю. Мне не хочется изучать все входы и выходы команды `find`, поэтому я использую ее только для поиска и перечисления файлов, а для всего остального я использую xargs. Ryan Thompson 15 лет назад 1
4
John T

Базовое имя принимает только один аргумент. Использование -execработает должным образом, поскольку каждое из них {}заменяется текущим обрабатываемым именем файла, и команда запускается один раз для каждого сопоставленного файла, вместо того, чтобы пытаться отправить все аргументы базовому имени за один раз.

2
Flet

xargs can be forced to just pass one argument as well...

find . -name '*.deb' -print | xargs -n1 basename

This works, however the accepted answer is using find in a more appropriate way. I found this question searching for xargs basename problems as I'm using another command to get a list of file locations. The -n1 flag for xargs was the ultimate answer for me.