16.2. find: busca archivos en función de ciertos criterios

find es un utilitario de Unix muy antiguo. Su rol es buscar recursivamente uno o más directorios y encontrar archivos que se correspondan con un cierto conjunto de criterios en esos directorios. Aunque es muy útil, su sintaxis es verdaderamente compleja, y usarlo requiere cierta práctica. La sintaxis general es:

find [opciones] [directorios] [criterios] [acción]

Si no especifica directorio alguno, find buscará en el directorio corriente. Si no especifica el criterio, esto es equivalente a "verdadero", por lo que se encontrarán todos los archivos. Las opciones, criterios y acciones son tan numerosas que solo mencionaremos algunas de cada una. Comencemos por las opciones:

  1. -xdev: No extender la búsqueda a los directorios ubicados en otros sistemas de archivos.

  2. -mindepth <n>: Descender al menos <n> niveles bajo el directorio especificado antes de comenzar a buscar los archivos.

  3. -maxdepth <n>: Buscar los archivos que se encuentran a lo sumo n niveles bajo el directorio especificado.

  4. -follow: Seguir los vínculos simbólicos si apuntan a directorios. Predeterminadamente, find, no los sigue.

  5. -daystart: Cuando se usan las pruebas relativas a la fecha y la hora (ver debajo), toma el comienzo del día corriente como etiqueta temporal en vez del predeterminado (24 horas antes de la hora corriente).

Un criterio puede ser uno o más de varias pruebas atómicas; algunas pruebas útiles son:

  1. -type <tipo>: Busca los archivos de un tipo dado; <tipo> puede ser uno de: f (archivo regular), d (directorio), l (vínculo simbólico), s (socket), b (archivo en modo de bloques), c (archivo en modo caracter) o p (tubería nombrada).

  2. -name <patrón>: Encontrar los archivos cuyo nombre se corresponde con el <patrón> dado. Con esta opción, se trata a <patrón> como un patrón de englobamiento del shell (vea el capítulo Patrones de englobamiento  del shell  y  expresiones  regulares).

  3. -iname <patrón>: Como -name, pero sin tener en cuenta la capitalización.

  4. -atime <n>, -amin <n>: Encontrar los archivos a los que se ha accedido en los últimos <n> días (-atime) o en los últimos <n> minutos (-amin). También puede especificar +<n> o -<n>, en cuyo caso la búsqueda se hará para los archivos accedidos respectivamente hace al menos o a lo sumo <n> días/minutos.

  5. -anewer <archivo>: Encontrar los archivos que han sido accedidos más recientemente que el archivo <archivo>

  6. -ctime <n>, -cmin <n>, -cnewer <archivo> Igual que para -atime, -amin y -anewer, pero se aplica a la última fecha en la cual se modificó el contenido del archivo <archivo>.

  7. -regex <patrón>: Como para -name, pero patrón se trata como una expresión regular.

  8. -iregex <patrón>: Como -regex, pero sin tener en cuenta la capitalización.

Existen muchas otras pruebas, debe referirse a la página man de find para más detalles. Para combinar las pruebas, Ud. puede utilizar uno de:

  1. <c1> -a <c2>: Verdadero si tanto <c1> como <c2> son verdaderas; -a está implícito, por lo tanto puede ingresar <c1> <c2> <c3> ... si quiere que todas las pruebas <c1>, <c2>, ... se verifiquen.

  2. <c1> -o <c2>: Verdadero si <c1> o <c2> o ambos son verdaderos. Note que -o tiene una precedencia menor que -a, por lo tanto si desea, por ejemplo, los archivos que verifican los criterios <c1> o <c2> y verifican el criterio <c3>, tendrá que usar paréntesis y escribir ( <c1> -o <c2> ) -a <c3>. Debe escapar (desactivar) los paréntesis, ya que si no lo hace ¡el shell los interpretará!

  3. -not <c1>: Invertir la prueba <c1>, por lo tanto -not <c1> es verdadero si <c1> es falso.

Finalmente, puede especificar una acción para cada archivo encontrado. Las acciones más usadas frecuentemente son:

  1. -print: Simplemente imprime el nombre de cada archivo en la salida estándar. Esta es la acción predeterminada si Ud. no especifica acción alguna.

  2. -ls: Imprime en la salida estándar el equivalente de ls -ilds para cada archivo que encuentra.

  3. -exec <comando>: Ejecutar el comando <comando> sobre cada archivo encontrado. La línea de comandos <comando> debe terminar con un ;, que deberá desactivar para que el shell no lo interprete; la posición del archivo se representa con {}. Vea los ejemplos de uso para entender mejor esto.

  4. -ok <comando>: Igual que -exec pero pedir confirmación para cada comando.

¿Todavía está aquí? Está bien, ahora practiquemos un poco, ya que todavía es la mejor forma de entender a este monstruo. Digamos que quiere encontrar todos los directorios en /usr/share. Entonces ingresará:

find /usr/share -type d

Suponga que tiene un servidor HTTP, todos sus archivos HTML están en /home/httpd/html, que coincide con su directorio corriente. Ud. desea encontrar todos los archivos que no se modificaron en el último mes. Debido a que tiene páginas de varios autores, algunos archivos tienen la extensión html y otros la extensión htm. Desea vincular estos archivos en el directorio /home/httpd/obsolete. Entonces ingresará:

find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 -exec ln {} /home/httpd/obsolete \;[1]

Está bien, este es uno un poco complejo y requiere una pequeña explicación. El criterio es este:

\( -name "*.htm" -o -name "*.html" \) -a -ctime -30

que hace lo que queremos: encuentra todos los archivos cuyos nombres terminan con .htm o con .html "\( -name "*.htm" -o -name "*.html" \)", y (-a) que no han sido modificados en los últimos 30 días, lo que es más o menos un mes (-ctime -30). Note los paréntesis: aquí son necesarios, porque -a tiene una precedencia mayor. Si no hubiera paréntesis alguno, se hubieran encontrado todos los archivos que terminen con .htm, y todos los archivos que terminen con .html y que no han sido modificados por un mes, que no es lo que nosotros queremos. Note también que los paréntesis están desactivados para el shell: si hubiésemos puesto ( .. ) en vez de \( .. \), el shell los hubiese interpretado y tratado de ejecutar -name "*.htm" -o -name "*.html" en un sub-shell... Otra solución podría haber sido poner los paréntesis entre comillas simples o dobles, pero aquí es preferible una contra-barra ya que solo tenemos que aislar un solo caracter.

Y finalmente, está el comando a ejecutar para cada uno de los archivos:

-exec ln {} /home/httpd/obsolete \;

Aquí también, tiene que desactivar el ; para el shell, ya que de no ser así el shell lo interpretaría como un separador de comandos. Si no lo hace, find se quejará que le falta un argumento a -exec.

Un último ejemplo: tiene un directorio enorme denominado /shared/images, con todo tipo de imágenes en él. Regularmente, Ud. usa el comando touch para actualizar la fecha de un archivo denominado stamp en este directorio, para que tenga una referencia temporal. Ud. quiere encontrar todas las imágenes JPEG en el mismo que son más nuevas que el archivo stamp, y ya que Ud. obtuvo las imágenes de varias fuentes, estos archivos tienen las extensiones jpg, jpeg, JPG o JPEG. También quiere evitar buscar en el directorio old. Quiere que se le envíe la lista de estos archivos por correo electrónico, y su nombre de usuario es juan:

find /shared/images -cnewer     \
     /shared/images/stamp       \
     -a -iregex ".*\.jpe?g"     \
     -a -not -regex ".*/old/.*" \
       | mail juan -s "Imagenes nuevas"

¡Y eso es todo! Por supuesto, este comando no es muy útil si tiene que ingresarlo cada vez, y quisiera ejecutarlo regularmente... Puede hacer lo siguiente:

Notas

[1]

Note que este ejemplo requiere que /home/httpd y /home/httpd/obsolete ¡estén en el mismo sistema de archivos!


Tux sobre Estrella por MandrakeSoft Linux es una marca registrada de Linus Torvalds. Todas las otras marcas registradas y copyrights son la propiedad de sus respectivos dueños.
A menos que se diga lo contrario, todo el contenido de estas páginas y todas las imágenes tienen Copyright de MandrakeSoft S.A. y de MandrakeSoft Inc. 2000.
http://www.linux-mandrake.com/