32blogby StudioMitsu

Guía práctica de find: domina la búsqueda de archivos en Linux

Aprende a usar el comando find para buscar archivos por nombre, tamaño, fecha y permisos. Cubre -exec, -prune para velocidad, lógica de condiciones y patrones reales para limpieza y automatización.

13 min read
Contenido

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.
  • -exec viene integrado. Puedes actuar sobre los resultados sin pasar por xargs.
  • Condiciones complejas. AND, OR, NOT, agrupación con paréntesis — find es 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

bash
# 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

bash
# 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

bash
# 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ónTimestampSignificado
-mtimemtimeFecha de modificación del contenido
-atimeatimeÚltimo acceso (lectura)
-ctimectimeCambio de metadatos (permisos, propietario)

La notación +/-

El número después de -mtime significa "períodos de 24 horas":

bash
# 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:

bash
# 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

bash
# 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

bash
# 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

bash
# 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

bash
# 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:

bash
# 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:

bash
# 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:

bash
# 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:

bash
# 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

bash
# 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:

bash
# 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:

bash
# 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

bash
# 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

bash
# Todos los archivos que NO son de .git
find . -not -path "*/.git/*"

# Forma corta: ! en lugar de -not
find . ! -name "*.tmp"

Agrupación compleja

bash
# 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

bash
# 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

bash
# 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

bash
# 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

bash
# 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

bash
# 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

bash
# 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:

EscenarioHerramientaPor qué
Búsqueda rápida por nombrefd~20x más rápido, sintaxis más simple
Filtrar por permisos/propietariofindfd no lo soporta
Consultas complejas por fechafind-mmin, -newer, etc.
Actuar sobre resultados (-exec)findIntegrado, sin pipe necesario
Respetar .gitignorefdActivado por defecto
Portabilidad de scriptsfindDisponible en todo sistema
Pipelines CI/CDfindSin instalación adicional
bash
# 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:

bash
# 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:

bash
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:

bash
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 nombre
  • find . -mtime +30 -delete — limpieza basada en fechas
  • find . -path "*/node_modules" -prune -o -name "*.ts" -print — saltar directorios para velocidad
  • find . -name "*.log" -exec gzip {} + — acciones batch sin pipes
  • find . -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.