¿Por qué el comando "buscar | grep 'filename' ”mucho más lento que“ find 'filename' ”?

10

Probé ambos comandos y el comando find | grep 'filename' es muchas veces más lento que el find 'filename' comando simple .

¿Cuál sería una explicación adecuada para este comportamiento?

yoyo_fun
fuente
2
Está enumerando cada archivo con find y luego pasando los datos a grep para procesar. Con find utilizado por sí mismo, se está perdiendo el paso de pasar todos los archivos enumerados a grep para analizar la salida. Por lo tanto, esto será más rápido.
Raman Sailopal
¿Más lento en qué sentido? ¿Los comandos tardan una cantidad diferente de tiempo en completarse?
Kusalananda
1
No puedo reproducir esto localmente. En todo caso, time find "$HOME" -name '.profile'informa más tiempo que time find "$HOME" | grep -F '.profile'. (17s vs 12s).
Kusalananda
2
@ JenniferAnderson Corrí los dos repetidamente. Los 17 y 12 segundos son promedios. Y sí, la grepvariación coincidirá en cualquier parte del findresultado, mientras que la coincidencia con find -namesolo coincidirá exactamente (en este caso).
Kusalananda
2
Sí, find filename sería rápida . Asumí que se trataba de un error tipográfico y que el OP significaba find -name filename. Con find filename, solo filenamesería examinado (y nada más).
Kusalananda

Respuestas:

11

(Estoy asumiendo GNU findaquí)

Usando solo

find filename

sería rápido, porque simplemente regresaría filename, o los nombres dentro filenamesi es un directorio, o un error si ese nombre no existiera en el directorio actual. Es una operación muy rápida, similar a ls filename(pero recursiva si filenamees un directorio).

A diferencia de,

find | grep filename

permitiría findgenerar una lista de todos los nombres del directorio actual y debajo, que grepluego se filtraría. Obviamente, esta sería una operación mucho más lenta.

Supongo que lo que realmente se pretendía era

find . -type f -name 'filename'

Esto buscaría filenamecomo el nombre de un archivo normal en cualquier parte del directorio actual o debajo.

Esto será tan rápido (o comparativamente rápido) como find | grep filename, pero la grepsolución coincidiría filenamecon la ruta completa de cada nombre encontrado, de manera similar a lo -path '*filename*'que haría con find.


La confusión proviene de un malentendido de cómo findfunciona.

La utilidad toma varias rutas y devuelve todos los nombres debajo de estas rutas.

Luego, puede restringir los nombres devueltos utilizando varias pruebas que pueden actuar sobre el nombre del archivo, la ruta, la marca de tiempo, el tamaño del archivo, el tipo de archivo, etc.

Cuando tu dices

find a b c

solicita findenumerar cada nombre disponible bajo las tres rutas a, by c. Si se trata de nombres de archivos normales en el directorio actual, se devolverán. Si alguno de ellos es el nombre de un directorio, se devolverá junto con todos los demás nombres dentro de ese directorio.

Cuando lo hago

find . -type f -name 'filename'

Esto genera una lista de todos los nombres en el directorio actual ( .) y debajo. Luego, restringe los nombres a los de los archivos normales, es decir, no a directorios, etc., con -type f. Luego hay una restricción adicional a los nombres que coinciden con el filenameuso -name 'filename'. La cadena filenamepuede ser un patrón global de nombre de archivo, como *.txt(¡solo recuerde citarlo!).

Ejemplo:

Lo siguiente parece "encontrar" el archivo llamado .profileen mi directorio de inicio:

$ pwd
/home/kk
$ find .profile
.profile

Pero, de hecho, solo devuelve todos los nombres en la ruta .profile(solo hay un nombre, y ese es el de este archivo).

Luego cdsubo un nivel e intento nuevamente:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

El findcomando ahora no puede encontrar ninguna ruta llamada .profile.

Sin embargo, si consigo que mire el directorio actual, y luego restrinja los nombres devueltos a solo.profile , también lo encuentra desde allí:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile
Kusalananda
fuente
1
find filenamevolvería solo filenamesi filenameno fuera del directorio de tipos (o fuera del directorio de tipos, pero no tuviera ninguna entrada en sí)
Stéphane Chazelas
2

Explicación no técnica: Buscar a Jack en una multitud es más rápido que buscar a todos en una multitud y eliminar a todos de consideración excepto a Jack.

S Renalds
fuente
El problema es que el OP espera que Jack sea la única persona en la multitud. Si es así, tienen suerte. find jackenumerará jacksi es un archivo llamado jack, o todos los nombres en el directorio si es un directorio. Es un malentendido de cómo findfunciona.
Kusalananda
1

Todavía no he entendido el problema, pero puedo proporcionar algunas ideas más.

Al igual que para Kusalananda, la find | grepllamada es claramente más rápida en mi sistema, lo que no tiene mucho sentido. Al principio asumí algún tipo de problema de amortiguación; que escribir en la consola ralentiza el tiempo hasta la próxima llamada al sistema para leer el siguiente nombre de archivo. Escribir en una tubería es muy rápido: aproximadamente 40MiB / s incluso para escrituras de 32 bytes (en mi sistema bastante lento; 300 MiB / s para un tamaño de bloque de 1MiB). Por lo tanto, supuse que findpuede leer del sistema de archivos más rápido al escribir en una tubería (o archivo) para que las dos operaciones que leen las rutas de los archivos y escriban en la consola puedan ejecutarse en paralelo (lo que, findcomo un proceso de un solo hilo, no puede hacer por sí solo.

Es findculpa

Comparando las dos llamadas

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

y

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

muestra que findhace algo increíblemente estúpido (sea lo que sea). Simplemente resulta ser bastante incompetente en la ejecución -name '*.txt'.

Puede depender de la relación entrada / salida

Puede pensar que find -namegana si hay muy poco que escribir. Pero esto solo se vuelve más vergonzoso find. Pierde incluso si no hay nada para escribir en contra de los archivos de 200K (13M de datos de tubería) para grep:

time find /usr -name lwevhewoivhol

findgrepaunque puede ser tan rápido como

Resulta que findla estupidez con nameno se extiende a otras pruebas. Use una expresión regular en su lugar y el problema desaparecerá:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Supongo que esto puede considerarse un error. ¿Alguien dispuesto a presentar un informe de error? Mi versión es find (GNU findutils) 4.6.0

Hauke ​​Laging
fuente
¿Qué tan repetibles son tus tiempos? Si realizó la -nameprueba primero, es posible que haya sido más lenta debido a que el contenido del directorio no está en caché. (Cuando se prueba -namey -regexme parece que tienen más o menos el mismo tiempo, al menos una vez el efecto de caché se ha tomado en consideración Por supuesto que sólo puede ser una versión diferente de. find...)
psmears
@psmears Por supuesto, he hecho estas pruebas varias veces. El problema de almacenamiento en caché se ha mencionado incluso en los comentarios a la pregunta antes de la primera respuesta. Mi findversión es find (GNU findutils) 4.6.0
Hauke ​​Laging el
¿Por qué es sorprendente que la adición se -name '*.txt'ralentice find? Tiene que hacer un trabajo extra, probando cada nombre de archivo.
Barmar
@Barmar Por un lado, este trabajo adicional se puede hacer extremadamente rápido. Por otro lado, este trabajo extra ahorra otro trabajo. findtiene que escribir menos datos. Y escribir en una tubería es una operación mucho más lenta.
Hauke ​​Laging
Escribir en un disco es muy lento, escribir en una tubería no es tan malo, solo se copia en un búfer del núcleo. Observe que en su primera prueba, escribir más de /dev/nullalguna manera usó menos tiempo del sistema.
Barmar
0

Aviso : supondré que te refieres find . -name filename(de lo contrario, estás buscando cosas diferentes; en find filenamerealidad busca en una ruta llamada nombre de archivo , que puede contener casi ningún archivo, por lo que sale muy rápido).


Supongamos que tiene un directorio que contiene cinco mil archivos. En la mayoría de los sistemas de archivos, estos archivos se almacenan en una estructura de árbol , lo que permite localizar rápidamente cualquier archivo.

Cuando se pregunta finda localizar un archivo cuyo nombre sólo requiere la comprobación, findse pida para que el archivo, y sólo ese archivo, el sistema de archivos subyacente, que leerá muy pocas páginas de la memoria de masa. Entonces, si el sistema de archivos vale la pena, esta operación se ejecutará mucho más rápido que atravesar todo el árbol para recuperar todas las entradas.

findSin embargo, cuando pides algo simple, eso es exactamente lo que haces, atraviesas todo el árbol y lees. Cada. Soltero. Entrada. Con directorios grandes, esto podría ser un problema (es exactamente la razón por la cual varios softwares, que necesitan almacenar muchos archivos en el disco, crearán "árboles de directorio" de dos o tres componentes de profundidad: de esta manera, cada hoja solo necesita contener menos archivos).

LSerni
fuente
-2

Supongamos que el archivo / john / paul / george / ringo / beatles existe y el archivo que está buscando se llama 'piedras'

find / stones

find comparará "beatles" con "piedras" y lo soltará cuando la "s" y la "b" no coincidan.

find / | grep stones

En este caso, find pasará '/ john / paul / george / ringo / beatles' a grep y grep tendrá que abrirse camino a través de todo el camino antes de determinar si es una coincidencia.

grep está haciendo mucho más trabajo, por lo que lleva más tiempo

Paranoico
fuente
1
¿Has probado eso?
Hauke ​​Laging
3
El costo de las comparaciones de cadenas (extremadamente simple y económico) está completamente eclipsado por el costo de IO (o simplemente syscall si está en caché) de las búsquedas en el directorio.
Mat
grep no es una comparación de cadenas, es una comparación de expresiones regulares, lo que significa que debe abrirse camino a través de toda la cadena hasta que encuentre una coincidencia o llegue al final. Las búsquedas en el directorio son las mismas pase lo que pase.
Paranoico
@Paranoid Hm, ¿de qué versión de find estás hablando? Aparentemente no es nada como el hallazgo al que estoy acostumbrado en Debian.
tubería