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:
-xdev
: No extender la búsqueda a los directorios
ubicados en otros sistemas de archivos.
-mindepth
<n>
: Descender al menos
<n>
niveles bajo el directorio
especificado antes de comenzar a buscar los archivos.
-maxdepth
<n>
: Buscar los archivos que se encuentran a lo
sumo n
niveles bajo el directorio
especificado.
-follow
: Seguir
los vínculos simbólicos si apuntan a directorios.
Predeterminadamente, find no los sigue.
-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 una o más de varias pruebas atómicas; algunas pruebas útiles son:
-type
<tipo_archivo>
: Busca los archivos de un tipo
dado. tipo_archivo
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).
-name
<patrón>
: Encontrar los archivos cuyo nombre se
corresponde con el patrón dado. Con esta opción, se trata al
patrón como un patrón de
englobamiento del shell (consulte Sección 3, “Patrones de englobamiento del
shell”).
-iname
<patrón>
: Como -name
, pero sin
tener en cuenta la capitalización.
-atime
<n>
, -amin <n>
: Encontrar
los archivos a los que se ha accedido por última vez hace
n
días (-atime
) o hace
n
minutos (-amin
). También
puede especificar <+n>
o
<-n>
, en cuyo caso la búsqueda se hará
para los archivos accedidos hace al menos o a lo sumo
n
días/minutos.
-anewer
<un_archivo>
: Encontrar los archivos que han sido
accedidos más recientemente que el archivo
un_archivo
.
-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.
-regex <patrón>
:
Como -name
, pero patrón
se trata como una expresión
regular.
-iregex
<patrón>
: Como -regex
, pero sin
distinguir entre mayúsculas y minúsculas.
Existen muchas otras pruebas, debe consultar find(1) para más detalles. Para combinar las pruebas, Usted puede utilizar uno de:
<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
y
c3
sean verdaderas.
<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á!
-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:
-print
:
Simplemente imprime el nombre de cada archivo en la salida
estándar. Esta es la acción predeterminada.
-ls
: Imprime en
la salida estándar el equivalente de ls -ilds
para cada archivo que encuentra.
-exec
<línea_de_comandos>
: Ejecutar el comando
línea_de_comandos
sobre cada archivo
encontrado. La línea de comandos
línea_de_comandos
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.
-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
/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á[29]:
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.
[29] Note que este
ejemplo necesita que /var/www
y
/var/www/obsolete
¡estén en el mismo sistema
de archivos!