¿Por qué encontrar a veces coincide con su argumento de ruta de línea de comando?

9

En Linux,

cd /tmp
mkdir foo; cd foo

Ahora corriendo

find . -name 'foo'

no da salida Mientras que correr

find /tmp/foo -name 'foo'

Da el resultado /tmp/fooque no tiene sentido para mí. ¿Alguien puede explicar por qué?

York
fuente
2
encontrar seches dentro del camino dado incluido como se indica. Entonces, si ingresó ./, no coincidiófoo
Costas
@ Costas: Entiendo eso. Lo que no entiendo es por qué ha marcado la diferencia si le doy el camino absoluto find.
York
@York En ciertas situaciones no existe un comportamiento que siempre sea correcto. Imagine un enlace simbólico con el nombre barque apunta a un archivo fooque está fuera de la ruta de búsqueda. ¿Esa coincidencia o no?
Hauke ​​Laging
1
Los .y /tmp/foono son lo mismo: son dos enlaces duros diferentes al mismo directorio; find /tmp/foo/. -name 'foo'tampoco encuentra nada.
jimmij
@jimmij: cuando ejecuto find /tmp/foo -name 'foo', le pedía a bash que buscara en el directorio /tmp/fooun archivo cuyo nombre es "foo". Como el directorio /tmp/fooestá vacío, no debería haber devuelto nada. No entiendo por qué vuelve /tmp/foo. Por otro lado, cuando corro find . -name 'foo', le estaba preguntando a bash lo mismo, es decir, encontrar un archivo en el directorio actual (que resultó ser /tmp/foo), cuyo nombre es 'foo', y no devuelve nada que tenga sentido.
York

Respuestas:

15

findatraviesa los árboles de directorios especificados y evalúa la expresión dada para cada archivo que encuentra. El recorrido comienza en el camino dado. Aquí hay un resumen de cómo find . -name foofunciona:

  • Primera ruta en la línea de comando: .
    • ¿El nombre base ( .) coincide con el patrón foo? No, entonces no hagas nada.
      Sucede que /tmp/fooes otro nombre para el mismo directorio. Pero findno lo sabe (y se supone que no debe intentar averiguarlo).
    • ¿Es la ruta un directorio? Sí, entonces atravesarlo. Enumere las entradas .y, para cada entrada, realice el proceso transversal.
      • El directorio está vacío: no contiene ninguna entrada que no sea .y .., que findno recorre recursivamente. Entonces el trabajo está terminado.

Y find /tmp/foo:

  • Primera ruta en la línea de comando: /tmp/foo
    • ¿El nombre base ( foo) coincide con el patrón foo? Sí, entonces la condición coincide.
      • No hay ninguna acción asociada con esta condición, por lo tanto, realice la acción predeterminada, que es imprimir la ruta.
    • ¿Es la ruta un directorio? Sí, entonces atravesarlo. Enumere las entradas /tmp/fooy, para cada entrada, realice el proceso transversal.
      • El directorio está vacío: no contiene ninguna entrada que no sea .y .., que findno recorre recursivamente. Entonces el trabajo está terminado.

Se da la circunstancia de que .y /tmp/fooson el mismo directorio, pero eso no es suficiente para garantizar que findtiene el mismo comportamiento en ambos. El findcomando tiene formas de distinguir entre rutas al mismo archivo; El -namepredicado es uno de ellos. find /tmp/foo -name foocoincide con el directorio de inicio, así como con cualquier archivo debajo que se llame foo. find . -name .solo coincide con el directorio inicial ( .nunca se puede encontrar durante un recorrido recursivo).

Gilles 'SO- deja de ser malvado'
fuente
"no contiene ninguna entrada que no sea. y ..., que find no atraviesa recursivamente". Supongo que "no atraviesa recursivamente" se refieren a "..". Si eso es correcto, ¿qué significaría "transversal recursivamente" en el contexto de ".."?
Faheem Mitha
@FaheemMitha "no atraviesa recursivamente" se aplica a ambos .y ... (Si se findatraviesa .recursivamente, terminaría yendo a través del directorio una y otra y otra vez y ...) .y ..no son solo anotaciones especiales, aparecen como entradas de directorio.
Gilles 'SO- deja de ser malvado'
5

No hay normalización de los argumentos de la línea de comando antes de que se apliquen las pruebas. Por lo tanto, los resultados difieren dependiendo de la ruta utilizada (si están involucrados enlaces simbólicos):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

En "su caso" ambas llamadas darían el mismo resultado que podría ser (más) confuso. Puede usarlo -mindepth 1si desea que se ignoren los puntos de partida (puede que no sean POSIX).

Hauke ​​Laging
fuente
-mindepth 1trabajos. Sin embargo, todavía no entiendo por qué find . -name 'foo'y me find /tmp/foo -name 'foo'comporto de manera diferente.
York
2

(gnu) find muestra las coincidencias encontradas dentro de la ruta proporcionada al comando porque comienza su comparación con los argumentos de la línea de comandos, descendiendo más profundamente en la estructura del directorio desde allí (por lo tanto, -maxdepth 0limita las pruebas al nivel base o solo a los argumentos de la línea de comandos, mientras que -mindepth 1omite los argumentos de la línea de comandos como se man findexplica Esta es la razón por la find /tmp/foo -name 'foo'cual producirá una coincidencia incluso si el directorio en sí está vacío.

find . -name 'foo'por otro lado, no dará ningún resultado porque .(punto) es un archivo especial que actúa como un enlace rígido al mismo inodo que /tmp/foo: es como un nombre de archivo separado (aunque especial) y no un enlace simbólico o una expresión sujeta a expansión del nombre de ruta por el shell. Por lo tanto, la primera prueba aplicada por find a los argumentos de la línea de comandos en el ejemplo dado no mostrará coincidencias, ya que de .hecho no coincide con el patrón de nombre definido en -name 'foo'. Tampoco lo hace, /tmp/foo/.ya que una prueba para un -namepatrón se realiza solo en el nombre base de la ruta (ver man find), que aquí está nuevamente ..

Si bien este comportamiento puede no esperarse o parecer intuitivo desde la perspectiva del usuario (y sí, al principio también me confundió), no constituye un error, sino que corresponde con la lógica y la funcionalidad descritas en las páginas de manual e información para ( ñu) encontrar.

Shevek
fuente
0

Intenté comentar la respuesta de Gilles, pero fue demasiado largo para caber en un comentario. Por esta razón, lo pongo como respuesta a mi propia pregunta.

La explicación de Gilles (y la de Shevek también) es clara y tiene sentido. El punto clave aquí es que no solo findintenta hacer coincidir los nombres de archivo para los archivos dentro de las rutas dadas (recursivamente), sino que también trata de hacer coincidir el nombre base de las rutas dadas.

Por otro lado, ¿hay alguna prueba de que esta es la forma en que findse supone que funciona, en lugar de ser un error? En mi opinión, sería mejor si no hace que los resultados sean inconsistentes find .y find ABSOLUTE-PATHporque la inconsistencia siempre es confusa y podría desperdiciar mucho tiempo al desarrollador tratando de descubrir "qué estaba mal". En mi caso, estaba escribiendo un guión, y la ruta se tomó de una variable. Entonces, para que mi script funcione correctamente, lo que puedo pensar es escribir find $path/. -name 'pattern'.

Finalmente, creo que se pueden obtener resultados consistentes findal sustituir siempre .el directorio actual por el directorio actual antes de continuar.

York
fuente
-1

No hay ningún objeto nombrado fooen el directorio donde inició la búsqueda relativa.

Está en lo cierto al suponer que gfindtiene errores cuando informa /tmp/foocuando se usa el nombre absoluto como directorio de inicio.

Gfindtiene varias desviaciones del estándar, parece que encontraste otra. Cuando desee una solución compatible más estándar, le recomiendo sfindque sea parte del schilytools.

-namese aplica a los resultados de búsqueda del directorio. Ninguno de los dos casos que menciona devolverá una entrada de directorio foode una readdir()operación.

astuto
fuente
Sospecho que esto también es un error. Aquí está el resultado de find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Licencia GPLv3 +: GNU GPL versión 3 o posterior < gnu.org/licenses/gpl.html >
York
Bueno, verifiqué sus comandos de ejemplo con find(certificado POSIX) gfindy sfind; el problema solo existe cuando lo usas gfind. En Linux, gfindno se instala con su nombre nativo sino con el nombre find.
schily
3
Es correcto para la find /tmp/foo -name foosalida /tmp/foo. (Cc @York) Este es el comportamiento de OpenBSD find, GNU find, BusyBox find, Solaris findy cualquier otro compatible con POSIX find("El primario evaluará como verdadero si el nombre base del nombre del archivo que se examina coincide con el patrón (...)" - cuando el nombre del archivo es uno que se pasa como un operando de ruta, no hay readdirllamadas involucradas).
Gilles 'SO- deja de ser malvado'