32blogby Studio Mitsu

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.

by omitsu14 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 (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

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 (-perm)findfd no lo soporta (tiene --owner para propietario)
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. 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:

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.