find: -exec vs xargs (también conocido como ¿Por qué se rompe "find | xargs basename"?)

10

Estaba tratando de encontrar todos los archivos de cierto tipo distribuidos en subdirectorios, y para mis propósitos solo necesitaba el nombre de archivo. Intenté eliminar el componente de ruta a través de basename, pero no funcionó con 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.

Me sale lo mismo (exactamente el mismo error) con cualquiera de estas variaciones:

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

Esto, por otro lado, funciona como se esperaba:

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

Esto sucede en Cygwin y Debian 5.0.3 actualizados. Mi diagnóstico es que xargs está pasando por alguna razón dos líneas de entrada al nombre base, pero ¿por qué? ¿Que está pasando aqui?

quijote curandero
fuente

Respuestas:

23

Porque basenamesolo quiere un parámetro ... no MUCHOS. Y xargscrea muchos parámetros.

Para resolver su problema real (solo enumere los nombres de archivo):

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

Que imprime solo el 'nombre base' (man find):

 %f     File's name with any leading directories
        removed (only the last element).
akira
fuente
1
oooh .... / palmadas frente de nuevo / Creo que necesito un libro "encontrar para los simulado" ...
charlatán quijote
pensé que el punto xargses que crea una lista de argumentos y alimenta cada uno al comando que viene después. de lo contrario, ¿cuál es la diferencia entre eso y find . -name '*.deb' | basename?
WindowsMaker
GNU basename ahora tiene una -aopción: "admite múltiples argumentos y trata cada uno como un nombre".
obispo
1
@WindowsMaker se xargsconvierte stdinen argumentos de comando. En cierto modo, es lo contrario de echo, lo que convierte sus argumentos en stdout. La diferencia entre find ... | xargs -n1 basenameo find ... | xargs basename -ay find ... | basenamees que los dos primeros funcionarán con implementaciones de basenameese ignorar stdin.
8bittree
19

Prueba esto:

find . -name '*.deb' | xargs -n1 basename
perlguy9
fuente
Esta no es la explicación, esta es una solución. y la solución es tan buena como simplemente llamar a 'basename' a través de -exec para cualquier archivo encontrado.
akira
44
+1 ... aunque no es una explicación, esto me llevaría a investigar el interruptor de xargs que muestra, lo que eventualmente me llevaría al movimiento de golpear la frente que acabo de usar para leer las respuestas de akira y john t ...
quack quixote
1
Así es como lo hago. No tengo ganas de aprender todos los entresijos del findcomando, por lo que solo lo uso para buscar y enumerar archivos, y uso xargs para todo lo demás.
Ryan C. Thompson
4

basename solo acepta un único argumento. El uso -execfunciona correctamente porque cada uno {}se reemplaza por el nombre de archivo actual que se está procesando y el comando se ejecuta una vez por archivo coincidente , en lugar de intentar enviar todos los argumentos a basename de una sola vez.

John T
fuente
3

xargs puede ser forzado a pasar un argumento también ...

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

Esto funciona, sin embargo, la respuesta aceptada se utiliza findde una manera más adecuada. Encontré esta pregunta buscando xargs basenameproblemas ya que estoy usando otro comando para obtener una lista de ubicaciones de archivos. La -n1bandera de xargsfue la respuesta definitiva para mí.

Flet
fuente