El comando find recorre recursivamente un árbol de directorios y devuelve cada archivo o directorio que cumpla tus criterios. Maneja patrones de nombre, marcas de tiempo, tamaños, permisos, y puede actuar sobre los resultados directamente — sin necesidad de pipes.
En resumen: find /ruta -name "*.log" -type f localiza todos los archivos .log bajo /ruta. Añade -exec para actuar sobre los resultados, -mtime para filtrar por antigüedad, -size para filtrar por tamaño, y -prune para saltar directorios. Combina condiciones con -and, -or y -not para consultas precisas.
Por qué find sigue siendo esencial
Quizás te preguntes: ¿por qué aprender find si fd existe y es 20 veces más rápido para búsquedas simples? Porque find hace cosas que fd no puede:
- Está en todas partes. Cada servidor Linux, cada contenedor Docker, cada runner de CI tiene
find. Sin instalación. -execviene integrado. Puedes actuar sobre los resultados sin pasar por xargs.- Condiciones complejas. AND, OR, NOT, agrupación con paréntesis —
findes un lenguaje de consultas para tu sistema de archivos. - Precisión de timestamps. Filtra por tiempo de modificación, acceso o cambio de estado al minuto.
- Búsqueda por permisos. Encuentra binarios SUID, archivos escribibles por todos, archivos de un usuario específico.
Yo uso fd para búsquedas rápidas, pero cuando necesito limpiar un servidor de build o auditar permisos en producción, find es la herramienta que elijo. En un deploy de 32blog, usé find para rastrear archivos de caché de .next que estaban comiendo 4GB de disco — fd no podía filtrar por tamaño y fecha de modificación en una sola pasada.
Lo básico: nombre, tipo y ruta
Buscar por nombre
# Buscar todos los archivos TypeScript
find . -name "*.ts"
# Búsqueda sin distinguir mayúsculas/minúsculas
find . -iname "*.readme"
-name usa patrones glob del shell (no regex). El patrón debe coincidir con el nombre completo — "*.ts" coincide con app.ts pero no con app.tsx.
Filtrar por tipo
# Solo archivos (no directorios)
find . -name "*.log" -type f
# Solo directorios
find /etc -type d -name "nginx"
# Solo enlaces simbólicos
find /usr/local/bin -type l
Valores comunes de -type: f (archivo), d (directorio), l (enlace simbólico).
Especificar ruta de inicio
# Buscar en múltiples directorios
find /var/log /tmp -name "*.log" -type f
# Buscar desde raíz (puede ser lento — considera -maxdepth)
find / -name "nginx.conf" 2>/dev/null
Filtrar por fecha: -mtime, -newer y compañía
El filtrado por tiempo es donde find realmente brilla. Cada archivo tiene tres marcas de tiempo:
| Opción | Timestamp | Significado |
|---|---|---|
-mtime | mtime | Fecha de modificación del contenido |
-atime | atime | Último acceso (lectura) |
-ctime | ctime | Cambio de metadatos (permisos, propietario) |
La notación +/-
El número después de -mtime significa "períodos de 24 horas":
# Modificados hace MÁS de 30 días (archivos viejos)
find /var/log -name "*.log" -mtime +30
# Modificados hace MENOS de 1 día (últimas 24 horas)
find . -name "*.mdx" -mtime -1
# Modificados EXACTAMENTE hace 7 días (entre 168 y 192 horas)
find . -mtime 7
Precisión al minuto
Reemplaza -mtime con -mmin para minutos en lugar de días:
# Archivos modificados en los últimos 15 minutos
find . -mmin -15
# Archivos NO accedidos en los últimos 60 minutos
find . -amin +60
Más nuevo que un archivo de referencia
# Archivos modificados después de deploy.log
find . -newer deploy.log
# Crear un marcador de tiempo y buscar archivos más nuevos
touch -t 202603200000 /tmp/marker
find /var/log -newer /tmp/marker
Filtrar por tamaño y permisos
Por tamaño
# Archivos mayores a 100MB
find / -type f -size +100M
# Archivos menores a 1KB
find . -type f -size -1k
# Archivos vacíos
find . -type f -empty
# Directorios vacíos
find . -type d -empty
Sufijos de tamaño: c (bytes), k (kilobytes), M (megabytes), G (gigabytes).
Por permisos
# Archivos escribibles por todos (auditoría de seguridad)
find / -type f -perm -o=w 2>/dev/null
# Binarios SUID (auditoría de seguridad)
find / -type f -perm -4000 2>/dev/null
# Archivos con permisos exactos 644
find . -type f -perm 644
# Archivos donde el propietario puede ejecutar
find . -type f -perm -u=x
La notación de -perm (consulta la guía de chmod/chown para lo básico de permisos):
-perm 644— coincidencia exacta (debe ser exactamente 644)-perm -644— todos estos bits deben estar activos (puede tener más)-perm /644— cualquiera de estos bits está activo
Por propietario
# Archivos del usuario "deploy"
find /var/www -user deploy
# Archivos del grupo "www-data"
find /var/www -group www-data
# Archivos sin propietario (huérfanos — el usuario fue eliminado)
find / -nouser 2>/dev/null
Actuar sobre los resultados: -exec, -delete y -print0
Encontrar archivos es solo la mitad del trabajo. find puede actuar sobre los resultados directamente.
-exec con ; (uno a uno)
Ejecuta el comando una vez por cada archivo:
# Dar permisos de ejecución a todos los scripts shell
find . -name "*.sh" -exec chmod +x {} \;
# Mostrar info detallada de cada .conf
find /etc -name "*.conf" -exec ls -la {} \;
{} se reemplaza por el nombre del archivo actual. \; termina el comando (la barra invertida escapa el punto y coma del shell).
-exec con + (modo batch)
Pasa tantos nombres de archivo como sea posible en una sola invocación — mucho más rápido:
# Contar líneas de todos los archivos Python (una sola llamada a wc)
find . -name "*.py" -exec wc -l {} +
# Cambiar propietario de todos los archivos de una vez
find /var/www -type f -exec chown deploy:www-data {} +
El terminador + agrupa argumentos como hace xargs. Úsalo siempre que el comando destino acepte múltiples argumentos.
-delete
Un atajo integrado para eliminar archivos:
# Eliminar todos los .tmp
find /tmp -name "*.tmp" -type f -delete
# Eliminar directorios vacíos
find ./build -type d -empty -delete
-print0 para pipes seguros
Cuando pasas resultados a xargs, usa -print0 para manejar nombres con espacios o caracteres especiales:
# Pipeline seguro: separado por bytes nulos
find . -name "*.log" -print0 | xargs -0 gzip
# Sin -print0, "mi archivo.txt" se divide en "mi" y "archivo.txt"
Controlar la profundidad: -maxdepth y -prune
Limitar la profundidad de búsqueda
# Solo el directorio actual (sin recursión)
find . -maxdepth 1 -name "*.txt"
# Directorio actual + un nivel abajo
find . -maxdepth 2 -type f -name "*.json"
# Saltar el primer nivel (buscar solo en subdirectorios)
find . -mindepth 2 -name "*.ts"
Podar directorios con -prune
-prune le dice a find que salte árboles de directorios completos — esencial para rendimiento:
# Buscar .ts pero saltar node_modules
find . -path "*/node_modules" -prune -o -name "*.ts" -print
# Saltar múltiples directorios
find . \( -path "./.git" -o -path "./node_modules" -o -path "./.next" \) -prune -o -name "*.ts" -print
El patrón es: -path "directorio_a_saltar" -prune -o <tus_condiciones> -print. El -o significa "o" — find o poda el directorio o continúa buscando.
En un proyecto Next.js como 32blog, saltar .next, node_modules y .git reduce el tiempo de búsqueda de 12 segundos a menos de 1 segundo.
Combinar condiciones: AND, OR, NOT
Por defecto, múltiples condiciones se combinan con AND:
# Archivos .log Y mayores a 10MB (AND implícito)
find /var/log -name "*.log" -size +10M
# AND explícito (mismo resultado)
find /var/log -name "*.log" -and -size +10M
Condiciones OR
# Archivos .jpg O .png
find . -name "*.jpg" -o -name "*.png"
# Con acción — necesitas agrupación (paréntesis)
find . \( -name "*.jpg" -o -name "*.png" \) -exec ls -la {} +
Condiciones NOT
# Todos los archivos que NO son de .git
find . -not -path "*/.git/*"
# Forma corta: ! en lugar de -not
find . ! -name "*.tmp"
Agrupación compleja
# Archivos TypeScript de más de 50KB modificados hoy, excluyendo tests
find src/ \( -name "*.ts" -o -name "*.tsx" \) \
-size +50k -mtime 0 \
-not -path "*/__tests__/*" \
-not -name "*.test.*"
Patrones del mundo real
Patrón 1: Limpiar artefactos de build
# Eliminar caché de .next de más de 7 días
find .next/cache -type f -mtime +7 -delete
# Eliminar node_modules de todos los proyectos en ~/projects
find ~/projects -maxdepth 3 -name "node_modules" -type d -prune -exec rm -rf {} +
Patrón 2: Encontrar archivos grandes que consumen disco
# Los 20 archivos más grandes del sistema
find / -type f -size +50M -exec ls -lhS {} + 2>/dev/null | head -20
# Archivos grandes en /var sin acceso en 90 días
find /var -type f -size +100M -atime +90 2>/dev/null
Patrón 3: Auditoría de seguridad
# Binarios SUID/SGID
find / -type f \( -perm -4000 -o -perm -2000 \) -exec ls -la {} + 2>/dev/null
# Directorios escribibles por todos (riesgo de seguridad)
find / -type d -perm -o=w -not -path "/tmp/*" -not -path "/var/tmp/*" 2>/dev/null
# Archivos modificados en las últimas 24 horas (respuesta a incidentes)
find / -type f -mtime 0 -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null
Patrón 4: Operaciones batch para proyectos web
# Convertir todos los PNG a WebP (usando -exec con shell)
find public/images -name "*.png" -exec sh -c 'cwebp -q 80 "$1" -o "${1%.png}.webp"' _ {} \;
# Corregir finales de línea en todos los archivos fuente
find src/ -name "*.ts" -exec dos2unix {} +
# Encontrar nombres de archivo duplicados entre locales
find content/ -name "*.mdx" -printf "%f\n" | sort | uniq -d
Patrón 5: Operaciones Git
# Encontrar archivos grandes no rastreados antes de hacer commit
find . -maxdepth 3 -size +5M -not -path "./.git/*" -not -path "*/node_modules/*" -type f
# Encontrar archivos que deberían estar en .gitignore
find . -name ".env*" -o -name "*.pem" -o -name "*.key" | grep -v ".git/"
Patrón 6: Gestión de logs
# Comprimir logs de más de 7 días
find /var/log -name "*.log" -mtime +7 -exec gzip {} \;
# Eliminar logs comprimidos de más de 90 días
find /var/log -name "*.log.gz" -mtime +90 -delete
# Logs modificados en la última hora (troubleshooting)
find /var/log -name "*.log" -mmin -60
find vs fd: cuándo usar cada uno
fd es una alternativa moderna escrita en Rust, significativamente más rápida para búsquedas por patrón. Así es como yo los uso:
| Escenario | Herramienta | Por qué |
|---|---|---|
| Búsqueda rápida por nombre | fd | ~20x más rápido, sintaxis más simple |
| Filtrar por permisos (-perm) | find | fd no lo soporta (tiene --owner para propietario) |
| Consultas complejas por fecha | find | -mmin, -newer, etc. |
| Actuar sobre resultados (-exec) | find | Integrado, sin pipe necesario |
Respetar .gitignore | fd | Activado por defecto |
| Portabilidad de scripts | find | Disponible en todo sistema |
| Pipelines CI/CD | find | Sin instalación adicional |
# Equivalente en fd de un find simple
fd "\.ts$" src/ # fd: rápido, limpio
find src/ -name "*.ts" # find: compatible en todas partes
# Solo find puede hacer esto
find / -type f -perm -4000 -mtime -1 -exec ls -la {} +
Para más sobre fd y otras herramientas modernas, consulta Herramientas CLI modernas en Rust. Para la referencia completa de cada opción de find, el manual de GNU findutils es el recurso más detallado.
FAQ
¿Cuál es la diferencia entre -name y -path?
-name coincide solo con el nombre del archivo (último componente). -path coincide con la ruta completa. Usa -path cuando necesites incluir la estructura de directorios: find . -path "*/config/*.json".
¿Cómo uso regex con find en lugar de globs?
Usa -regex en lugar de -name. Por defecto, find usa regex estilo Emacs y coincide contra la ruta completa:
# Buscar archivos .ts o .tsx con regex
find . -regex ".*\.\(ts\|tsx\)$"
# Usar regex extendida (POSIX ERE) para sintaxis más limpia
find . -regextype posix-extended -regex ".*\.(ts|tsx)$"
¿Por qué aparecen errores "Permission denied"?
Estás buscando en directorios sin permisos de lectura. Redirige stderr: find / -name "archivo" 2>/dev/null. O usa -readable (extensión GNU) para saltar entradas sin acceso: find / -readable -name "archivo".
¿Cuál es la diferencia entre -exec ; y -exec +?
\; ejecuta el comando una vez por archivo. + agrupa archivos en menos invocaciones (como xargs). + es casi siempre más rápido. Usa \; solo cuando necesites comportamiento individual por archivo (como renombrar con un comando shell).
¿Cómo hago que find siga enlaces simbólicos?
Usa el flag -L: find -L /ruta -name "*.conf". Por defecto, find no sigue enlaces simbólicos. Ten cuidado — seguir enlaces puede causar bucles infinitos si hay enlaces circulares.
¿Es seguro usar find -delete?
Es seguro si primero pruebas tu consulta con -print. find -delete procesa archivos en orden de profundidad y los elimina permanentemente — no hay papelera. Siempre ejecuta find <condiciones> -print antes de reemplazar -print con -delete.
¿Cómo busco archivos por contenido (no por nombre)?
find por sí solo no busca contenido. Combínalo con grep:
find . -name "*.ts" -exec grep -l "useState" {} +
O usa grep -r / rg directamente para búsquedas de contenido.
¿Puedo limitar cuántos resultados devuelve find?
find no tiene un límite integrado, pero puedes hacer pipe a head:
find . -name "*.log" | head -5
Ten en cuenta que find sigue ejecutándose después de que head obtiene suficientes resultados (recibe un SIGPIPE). Para búsquedas grandes donde solo quieres el primer resultado, -quit (extensión GNU) para inmediatamente: find . -name "objetivo" -print -quit.
Conclusión
find es uno de esos comandos que mejora cuanto más lo practicas. Los patrones que más vas a usar:
find . -name "*.ext" -type f— lo fundamental, buscar archivos por nombrefind . -mtime +30 -delete— limpieza basada en fechasfind . -path "*/node_modules" -prune -o -name "*.ts" -print— saltar directorios para velocidadfind . -name "*.log" -exec gzip {} +— acciones batch sin pipesfind . -name "*.txt" -print0 | xargs -0 comando— pipeline seguro hacia xargs
Si ya conoces xargs y grep, agregar find completa el trío de procesamiento de archivos. Combínalos con sed/awk para transformaciones y fzf para selección interactiva, y tendrás un flujo de trabajo que cubre la mayoría de tareas del sistema de archivos sin salir de la terminal.