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:
-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/propietario | find | fd no lo soporta |
| 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.
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.