find: -exec vs xargs (Pourquoi est-ce que “find | xargs basename” se rompt?)

10

J'essayais de trouver tous les fichiers d'un certain type répartis dans des sous-répertoires et pour mes besoins, je n'avais besoin que du nom de fichier. J'ai essayé de supprimer le composant de chemin via basename, mais ça n'a pas marché avec 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.

Je reçois la même chose (exactement la même erreur) avec l'une de ces variantes:

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

Ceci, en revanche, fonctionne comme prévu:

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

Cela se produit avec les versions récentes de Cygwin et Debian 5.0.3. Mon diagnostic est que xargs passe pour une raison quelconque en passant deux lignes d’entrée à basename, mais pourquoi? Que se passe t-il ici?

quack quixote
la source

Réponses:

22

Parce que basename veut juste un paramètre ... pas beaucoup de. Et xargs crée beaucoup de paramètres.

Pour résoudre votre problème réel (ne listez que les noms de fichiers):

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

Ce qui n'imprime que le 'nom de base' (man find):

 %f     File's name with any leading directories
        removed (only the last element).
akira
la source
1
oooh .... / tape à nouveau le front / Je pense avoir besoin d'un livre "trouver pour les nuls" ...
quack quixote
je pensais que le but de xargs Est-ce que cela crée une liste d'arguments et alimente chacun avec la commande qui vient après? sinon quelle est la différence entre cela et find . -name '*.deb' | basename ?
WindowsMaker
Le nom de base GNU a maintenant un -a option: "prendre en charge plusieurs arguments et les traiter chacun comme un nom".
bishop
1
@WindowsMaker xargs convertis stdin commander des arguments. D'une certaine manière, c'est l'opposé de echo, qui convertit ses arguments en stdout. La différence entre find ... | xargs -n1 basename ou find ... | xargs basename -a et find ... | basename est que les deux premiers vont travailler avec des implémentations de basename qui ignore stdin.
8bittree
17

Essaye ça:

find . -name '*.deb' | xargs -n1 basename
perlguy9
la source
ce n'est pas l'explication, c'est une solution de contournement. et la solution de contournement consiste à appeler simplement 'nom de base' via -exec pour tout fichier trouvé.
akira
4
+1 ... bien que ce ne soit pas une explication, cela m'amènerait à enquêter sur le commutateur xargs que vous montrez, ce qui me mènerait finalement au mouvement de frappe au front que je viens d'utiliser pour lire les réponses d'Akira et de John t ...
quack quixote
1
C'est comme ça que je le fais. Je n'ai pas envie d'apprendre tous les tenants et les aboutissants de la find commande, donc je ne l’utilise que pour rechercher et lister des fichiers, et j’utilise xargs pour tout le reste.
Ryan Thompson
4

nom_base n'accepte qu'un seul argument. En utilisant -exec fonctionne correctement car chaque {} est remplacé par le nom de fichier en cours de traitement et la commande est exécutée une fois par fichier correspondant , au lieu d’essayer d’envoyer tous les arguments à basename en une fois.

John T
la source
2

xargs peut être forcé de simplement passer un argument aussi ...

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

Cela fonctionne, mais la réponse acceptée utilise find d'une manière plus appropriée. J'ai trouvé cette question à la recherche de xargs basename problèmes que j'utilise une autre commande pour obtenir une liste des emplacements de fichiers. le -n1 drapeau pour xargs était la réponse ultime pour moi.

Flet
la source