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

10

Я пытался найти все файлы определенного типа, распределенные в подкаталогах, и для моих целей мне нужно было только имя файла. Я попытался удалить компонент пути через 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, но почему? Что тут происходит?

шарлатан
источник

Ответы:

23

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

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

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

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

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

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

find . -name '*.deb' | xargs -n1 basename
perlguy9
источник
это не объяснение, это обходной путь. и обходной путь так же хорош, как простой вызов 'basename' через -exec для любого найденного файла.
Акира
4
+1 ... хотя это и не объяснение, это привело бы меня к исследованию переключателя xargs, который вы показываете, что в конечном итоге привело бы меня к хлопающему по лбу движению, которое я только что использовал, читая ответы
Акиры
1
Вот как я это делаю. Мне не хочется изучать все входы и выходы findкоманды, поэтому я использую ее только для поиска и перечисления файлов, а для всего остального я использую xargs.
Райан С. Томпсон
4

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

Джон Т
источник
3

xargs можно заставить просто передать один аргумент, а также ...

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

Это работает, однако принятый ответ используется findболее подходящим способом. Я нашел этот вопрос в поиске xargs basenameпроблем, так как я использую другую команду для получения списка расположений файлов. -n1Флаг xargsбыл окончательный ответ для меня.

Flet
источник