find: -exec vs xargs (aka Warum bricht "find | xargs basename"?)

10

Ich habe versucht, alle Dateien eines bestimmten Typs in Unterverzeichnissen zu finden, und für meine Zwecke brauchte ich nur den Dateinamen. Ich habe versucht, die Pfadkomponente über zu basenameentfernen, aber es hat nicht funktioniert mit 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.

Ich bekomme das gleiche (genau den gleichen Fehler) mit einer dieser Varianten:

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

Dies funktioniert andererseits wie erwartet:

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

Dies geschieht unter Cygwin und Debian 5.0.3. Meine Diagnose lautet, dass xargs aus irgendeinem Grund zwei Eingabezeilen an den Basisnamen übergibt, aber warum? Was ist denn hier los?

Quacksalber
quelle

Antworten:

23

Weil basenamenur ein Parameter will ... nicht VIEL. Und xargserstellt viele Parameter.

So lösen Sie Ihr eigentliches Problem (listen Sie nur die Dateinamen auf):

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

Welches druckt nur den 'Basisnamen' (Mann finden):

 %f     File's name with any leading directories
        removed (only the last element).
Akira
quelle
1
oooh .... / schlägt wieder auf die Stirn / ich glaube ich brauche ein "Find for Dummies" Buch ...
Quack Quijote
Ich dachte, der Punkt xargsist, dass es eine Liste von Argumenten erstellt und jedes an den Befehl weiterleitet, der danach kommt? sonst was ist der Unterschied zwischen dem und find . -name '*.deb' | basename?
WindowsMaker
Der GNU-Basisname hat jetzt die -aOption: "Mehrere Argumente unterstützen und jedes als Namen behandeln".
Bischof
1
@WindowsMaker xargskonvertiert stdinin Befehlsargumente. In gewisser Weise ist es das Gegenteil von echo, das seine Argumente in umwandelt stdout. Der Unterschied zwischen find ... | xargs -n1 basenameoder find ... | xargs basename -aund find ... | basenamebesteht darin, dass die beiden ersteren mit Implementierungen basenamedieses Ignorierens arbeiten stdin.
8bittree
19

Versuche dies:

find . -name '*.deb' | xargs -n1 basename
perlguy9
quelle
Dies ist nicht die Erklärung, dies ist eine Problemumgehung. und die Problemumgehung ist so gut wie das Aufrufen von 'basename' über -exec für jede gefundene Datei.
Akira
4
+1 ... obwohl dies keine Erklärung ist, würde dies mich dazu bringen, den von Ihnen gezeigten Xargs-Schalter zu untersuchen, was mich schließlich zu der Bewegung führen würde, bei der ich gerade die Antworten von Akira und John T gelesen habe ...
Quacksalber 8.
1
So mache ich es. Ich habe keine Lust, alle Vor- und Nachteile des findBefehls zu lernen , daher verwende ich ihn nur zum Suchen und Auflisten von Dateien, und ich verwende xargs für alles andere.
Ryan C. Thompson
4

Basisname akzeptiert nur ein einziges Argument. Die Verwendung -execfunktioniert ordnungsgemäß, da jeder {}durch den aktuell verarbeiteten Dateinamen ersetzt wird und der Befehl einmal pro übereinstimmender Datei ausgeführt wird , anstatt zu versuchen, alle Argumente auf einmal an den Basisnamen zu senden.

John T.
quelle
3

xargs kann gezwungen werden, auch nur ein Argument zu übergeben ...

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

Dies funktioniert, die akzeptierte Antwort wird findjedoch angemessener verwendet. Ich habe diese Frage bei der Suche nach xargs basenameProblemen gefunden, da ich einen anderen Befehl verwende, um eine Liste der Dateispeicherorte abzurufen. Die -n1Flagge für xargswar die ultimative Antwort für mich.

Flet
quelle