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 чомусь передає два вхідні рядки до базового імені, але чому? Що тут відбувається?

Відповіді:


23

Тому що basenameхоче лише один параметр ... не багато. І xargsстворює безліч параметрів.

Щоб вирішити свою справжню проблему (перелічіть лише імена файлів):

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

Яке друкує лише "базову назву" (man find):

 %f     File's name with any leading directories
        removed (only the last element).

1
ооо .... / знову ляпає чолом / я думаю, мені потрібна книга "знайти для манекенів" ...
крякати кіхот

Я подумав, що справа xargsполягає в тому, що він створює список аргументів і подає кожен до команди, що приходить після? інакше яка різниця між тим і find . -name '*.deb' | basename?
WindowsMaker

GNU basename тепер має -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

це не пояснення, це рішення. і спосіб вирішення настільки ж хороший, як просто виклик 'basename' через -exec для будь-якого знайденого файлу.
akira

4
+1 ... поки не пояснення, це призвело б мене розслідувати xargs перемикача ви показуєте, що в кінцевому рахунку призведе мене до руху чола ляскаючи я використовував тільки читання Акіра і John T відповіді ...
шарлатан Кіхот

1
Ось як я це роблю. Мені не здається, що я вивчаю всі входи та мінуси findкоманди, тому я використовую її лише для пошуку та переліку файлів, а для всього іншого я використовую xargs.
Райан К. Томпсон

4

basename приймає лише один аргумент. Використання -execпрацює належним чином, тому що кожне {}замінюється поточним іменем файлу, яке обробляється, і команда виконується один раз за відповідний файл , замість того, щоб намагатися надсилати всі аргументи до базового імені за один раз.


3

xargs може бути змушений просто передати один аргумент ...

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

Це працює, проте прийнята відповідь використовується findбільш підходящим способом. Я знайшов це питання в пошуку xargs basenameпроблем, оскільки я використовую іншу команду, щоб отримати список файлів. -n1Прапор xargsбув остаточну відповідь для мене.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.