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

15713
quack quixote

Ich habe versucht, alle Dateien eines bestimmten Typs in Unterverzeichnissen zu finden, und für meine Zwecke benötigte ich nur den Dateinamen. Ich habe versucht, die Pfadkomponente über zu basenameentfernen, aber es funktionierte nicht 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 dasselbe (genau den gleichen Fehler) mit einer dieser Varianten:

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

Dies funktioniert hingegen wie erwartet:

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

Dies geschieht auf dem neuesten Stand von Cygwin und Debian 5.0.3. Meine Diagnose lautet, dass xargs aus irgendeinem Grund zwei Eingabezeilen an basename weitergibt, aber warum? Was ist denn hier los?

10

4 Antworten auf die Frage

22
akira

Da basenamewill nur ein Parameter ... nicht viel. Und xargserstellt viele Parameter.

Um Ihr echtes Problem zu lösen (listen Sie nur die Dateinamen auf):

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

Welche druckt nur den 'basename' (man find):

 %f File's name with any leading directories removed (only the last element). 
oooh.... */slaps forehead again/* i think i need a "find for dummies" book... quack quixote vor 15 Jahren 1
Ich dachte, der Sinn von `xargs` ist, dass es eine Liste von Argumenten erstellt und jedes auf den Befehl bezieht, der danach kommt? Ansonsten, was ist der Unterschied zwischen dem und `find. -name '* .deb' | basename`? WindowsMaker vor 7 Jahren 0
Der GNU-Basisname hat jetzt die Option "-a": "Mehrere Argumente unterstützen und jedes als Namen behandeln". bishop vor 7 Jahren 0
@WindowsMaker `xargs` konvertiert` stdin` in Befehlsargumente. In gewisser Weise ist es das Gegenteil von "echo", das seine Argumente in "stdout" umwandelt. Der Unterschied zwischen `find ... | xargs -n1 basename` oder `find ... | xargs basename -a` und `find ... | basename` ist, dass die vorherigen beiden mit Implementierungen von `basename` arbeiten, die` stdin` ignorieren. 8bittree vor 6 Jahren 1
17
perlguy9

Versuche dies:

find . -name '*.deb' | xargs -n1 basename 
Dies ist nicht die Erklärung, dies ist eine Problemumgehung. Die Problemumgehung ist so gut wie das Aufrufen von 'basename' über -exec für jede gefundene Datei. akira vor 15 Jahren 0
+1 ... obwohl dies keine Erklärung ist, würde ich den Xargs-Schalter untersuchen, den Sie anzeigen, was mich schließlich zu der Stirn-Ohrfeigen-Bewegung führen würde, in der ich gerade die Antworten von Akira und John t gelesen habe ... quack quixote vor 15 Jahren 4
So mache ich es. Ich habe nicht das Gefühl, alle Besonderheiten des `find`-Befehls zu lernen, also benutze ich es nur zum Suchen und Auflisten von Dateien und xargs für alles andere. Ryan Thompson vor 15 Jahren 1
4
John T

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

2
Flet

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

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

Dies funktioniert zwar, aber die akzeptierte Antwort wird findangemessener verwendet. Ich habe diese Frage auf 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.