2. find: Busca archivos en función de ciertos criterios

find es un utilitario de UNIX® muy antiguo. Su rol es recorrer 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 arcana, y usarlo requiere cierta práctica. La sintaxis general es:

find [opciones] [directorios] [criterio1] ... [criterioN] [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:

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

Existen muchas otras pruebas, debe consultar find(1) para más detalles. Para combinar las pruebas, Usted puede utilizar uno de:

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

¿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 /var/www/html, que coincide con su directorio corriente. Usted 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 /var/www/obsolete. Entonces ingresará[20]:

find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 \
-exec ln {} /var/www/obsolete \;

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 subshell... Otra solución podría haber sido poner los paréntesis entre comillas simples o dobles, pero aquí es preferible una contrabarra ya que simplemente tenemos que aislar un caracter solo.

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

-exec ln {} /var/www/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á de 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, Usted usa el comando touch para actualizar la fecha de un archivo denominado stamp en este directorio, para que tenga una referencia temporal. Usted quiere encontrar todas las imágenes JPEG en el mismo que son más nuevas que el archivo stamp, y ya que Usted 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 peter:

find /shared/images -cnewer     \
/shared/images/stamp       \
-a -iregex ".*\.jpe?g"     \
-a -not -regex ".*/old/.*" \
| mail peter -s "Imágenes nuevas"

Por supuesto, este comando no es muy útil si tiene que ingresarlo cada vez, y quisiera ejecutarlo regularmente... Puede programar la ejecución de comandos.



[20] Note que este ejemplo necesita que /var/www y /var/www/obsolete ¡estén en el mismo sistema de archivos!