¿Cómo obtener solo el nombre del archivo con Linux 'find'?

266

Estoy usando find para todos los archivos en el directorio, así que obtengo una lista de rutas. Sin embargo, solo necesito nombres de archivo. es decir, consigo ./dir1/dir2/file.txty quiero llegarfile.txt

marverix
fuente

Respuestas:

359

En GNU findpuede usar -printfparámetros para eso, por ejemplo:

find /dir1 -type f -printf "%f\n"
SiegeX
fuente
9
Claramente la respuesta , pero carece de detalles.
Jason McCreary
25
esto es solo para GNU
Osama F Elias
3
Esto no funciona para mí cuando uso varios tipos de archivos (-o switch)
Urchin
9
find: -printf: primario u operador desconocido
holms
@Urchin No hay razón para que no debería, siempre y cuando usted tiene lógica correcta (es decir, -otiene menor precedencia que implicaba -a, por lo que a menudo tendrá que agrupar sus -oargumentos)
Restablecer Mónica favor,
157

Si su búsqueda no tiene una opción -printf, también puede usar basename:

find ./dir1 -type f -exec basename {} \;
Kambus
fuente
17
Citar el punto y coma es otra forma de desambiguar:... {} ';'
davidchambers
28

Uso -execdirque contiene automáticamente el archivo actual {}, por ejemplo:

find . -type f -execdir echo '{}' ';'

También puede usar en $PWDlugar de .(en algunos sistemas no producirá un punto extra en el frente).

Si aún tiene un punto extra, también puede ejecutar:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

El -execdirprimario es idéntico al -execprimario con la excepción de que la utilidad se ejecutará desde el directorio que contiene el archivo actual .

Cuando se usa en +lugar de ;, {}se reemplaza con la mayor cantidad de rutas posibles para cada invocación de la utilidad. En otras palabras, imprimirá todos los nombres de archivo en una línea.

kenorb
fuente
Me estoy poniendo en ./filenamelugar de filename. Dependiendo de sus necesidades, puede o no estar bien.
user276648
@ user276648 Pruebe con en $PWDlugar de ..
kenorb
24

Si está utilizando GNU encuentre

find . -type f -printf "%f\n"

O puede usar un lenguaje de programación como Ruby (1.9+)

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

Si te apetece una solución bash (al menos 4)

shopt -s globstar
for file in **; do echo ${file##*/}; done
Kurumi
fuente
Tu respuesta me inspiró para ayudar a sleske: serverfault.com/a/745968/329412
Nolwennig
11

Si desea ejecutar alguna acción solo contra el nombre del archivo, el uso basenamepuede ser difícil.

Por ejemplo esto:

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

solo hará eco de nombre base /my/found/path. No es lo que queremos si queremos ejecutar el nombre del archivo.

Pero puedes entonces xargsla salida. por ejemplo, para eliminar los archivos en un directorio basado en nombres en otro directorio:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
j03m
fuente
no echo -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike
7

En mac (BSD find) use:

find /dir1 -type f -exec basename {} \;
espectro
fuente
1

-execy -execdirson lentos, xargses rey.

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargsEl paralelismo también ayuda.

Curiosamente, no puedo explicar el último caso de xargsfuera -n1. Da el resultado correcto y es el más rápido.¯\_(ツ)_/¯

( basenametoma solo 1 argumento de ruta pero xargslos enviará a todos (en realidad 5000) sin -n1. no funciona en Linux y OpenBSD, solo macOS ...)

Algunos números más grandes de un sistema Linux para ver cómo -execdirayuda, pero aún mucho más lento que un paralelo xargs:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system
menos
fuente
1
solo un punto de datos más: en openbsd a largo plazo findes -execdirque se convierte en el más rápido ya que crear nuevos procesos es una operación relativamente costosa.
menos
1

Honestamente basenamey dirnamesoluciones son más fáciles, pero también se puede comprobar esto:

find . -type f | grep -oP "[^/]*$"

o

find . -type f | rev | cut -d '/' -f1 | rev

o

find . -type f | sed "s/.*\///"
Hombre libre
fuente
0

Como otros han señalado, puede combinar findy basename, pero de manera predeterminada, el basenameprograma solo funcionará en una ruta a la vez, por lo que el ejecutable tendrá que iniciarse una vez para cada ruta (usando find ... -execo find ... | xargs -n 1), lo que puede ser lento.

Si usa la -aopción activada basename, puede aceptar múltiples nombres de archivo en una sola invocación, lo que significa que puede usar xargssin el -n 1, para agrupar las rutas en un número mucho menor de invocaciones basename, lo que debería ser más eficiente.

Ejemplo:

find /dir1 -type f -print0 | xargs -0 basename -a

Aquí he incluido el -print0y -0(que deben usarse juntos), para hacer frente a cualquier espacio en blanco dentro de los nombres de archivos y directorios.

Aquí hay una comparación de tiempos, entre las versiones xargs basename -ay xargs -n1 basename. (En aras de una comparación de igual a igual, los tiempos que se informan aquí son posteriores a una ejecución ficticia inicial, de modo que ambos se realizan después de que los metadatos del archivo ya se hayan copiado en la caché de E / S). cksumen ambos casos, solo para demostrar que la salida es independiente del método utilizado.

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

Como puede ver, es mucho más rápido evitar el lanzamiento basenamecada vez.

alaniwi
fuente
Al leer la respuesta de @minusf más a fondo, veo que en una Mac basenameaceptará múltiples nombres de archivos sin necesidad de argumentos adicionales en la línea de comandos. El uso de -aaquí está en Linux. ( basename --versionme dice basename (GNU coreutils) 8.28)
alaniwi
-3

He encontrado una solución (en la página de makandracards), que da solo el nombre de archivo más reciente:

ls -1tr * | tail -1

(gracias a Arne Hartherz)

Lo usé para cp:

cp $(ls -1tr * | tail -1) /tmp/
jazzinka
fuente
2
Esto no responde a la pregunta en absoluto.
kenorb