# Cómo buscar archivos y texto utilizando find y grep

**Categoría:** Linux y sistemas · **Nivel:** Inicial · **Lectura:** 14 min
**Publicada:** 2026-07-29 · **Actualizada:** 2026-07-29 · **Autoría:** Underc0de, con aporte original de TerminusRoot
**Versión HTML (canónica):** https://underc0de.org/guias/linux/buscar-archivos-y-texto-con-find-y-grep/

find localiza archivos por sus atributos y grep busca dentro del contenido: cómo se usa cada uno de verdad, y por qué combinarlos con tuberías es donde aparece toda la potencia.

## Respuesta rápida

> `find` y `grep` resuelven problemas distintos y se complementan. `find` recorre un árbol de directorios y filtra por los metadatos de cada entrada: nombre, tipo, fecha de modificación, tamaño. `grep` lee el contenido de los archivos y filtra por un patrón de texto o una expresión regular. Ninguno reemplaza al otro: `find` no mira qué hay escrito adentro de un archivo, y `grep` no filtra por fecha ni por tamaño. La potencia real aparece al combinarlos con una [tubería](../redirecciones-y-tuberias-en-la-terminal/index.md), del tipo `find ... -print0 | xargs -0 grep ...`, el patrón que evita que un nombre de archivo con espacios rompa todo el proceso.

## ¿Qué diferencia hay entre find y grep?

En una **terminal** —la interfaz de texto donde se escriben comandos, uno por línea— `find` y `grep` están entre los comandos que más se usan para ubicar algo entre miles de archivos, y aparecen en cualquier repaso de [comandos útiles de Linux](../comandos-utiles-de-linux/index.md). Pero apuntan a preguntas distintas, y confundirlas es la causa de la mayoría de las búsquedas fallidas.

`find` responde preguntas sobre los **atributos** de un archivo o directorio: nombre, tipo, fecha de modificación, tamaño. Recorre el árbol de directorios y evalúa cada entrada, sin abrir nunca el archivo para ver qué hay escrito adentro.

`grep` —de *global regular expression print*, heredado de un comando del editor `ed`— hace lo opuesto: lee cada archivo línea por línea y lo compara contra un **patrón de texto** o una **expresión regular** (una secuencia que describe un conjunto de cadenas posibles, no una sola palabra). Su salida son líneas que coinciden, no rutas.

- **-exec {} \; frente a {} +:** uno ejecuta el comando por archivo; el otro agrupa varios en menos invocaciones y suele ser más eficiente.
- **-print0 y xargs -0:** sin este par, un nombre con espacios se corta en dos argumentos al pasar por una tubería.
- **BRE, ERE y PCRE:** en las básicas hay que escapar `+`, `?`, `|` y paréntesis; en las extendidas (`-E`) son directos; PCRE (`-P`) no es universal.

Para atributos, `find`; para contenido, `grep`; para las dos cosas a la vez, una tubería.

![Diagrama de dos paneles y una tubería. El panel izquierdo, find, filtra rutas de archivo por metadatos: nombre con -name o -iname, tipo con -type, fecha de modificación con -mtime y tamaño con -size; su salida son rutas de archivo. El panel derecho, grep, filtra por contenido: un patrón de texto o expresión regular, con opciones -i para ignorar mayúsculas, -n para el número de línea y -E para expresiones regulares extendidas; su salida son líneas de texto que coinciden. Debajo, un tercer bloque muestra la tubería find . -type f -name "*.log" -print0 seguido de xargs -0 grep -l "error", con -print0 y -0 remarcados en celeste: ese byte nulo evita que un nombre con espacios se corte en dos argumentos.](../../assets/img/guias/find-y-grep.svg)

*find filtra por metadatos y grep filtra por contenido; la tubería con -print0 y xargs -0 es la forma segura de unir ambos filtros.*

## Cómo buscar archivos con find

La sintaxis general de `find` es `find [ruta...] [expresión]`. Si no indicás una ruta, busca desde el directorio actual. La expresión combina **pruebas** (condiciones sobre cada entrada) y, si hace falta, **acciones** sobre lo que encuentra; sin una acción explícita, `find` simplemente imprime la ruta de cada coincidencia.

```bash
find . -name "*.log"
find /var/log -type f -name "*.log"
find . -type d -name "node_modules"
find /home/usuario -mtime -7
find . -size +100M
find /var/www -type f -name "*.php" -mtime -1
```

La primera busca archivos `.log` desde el directorio actual; la segunda limita el tipo y la ruta a `/var/log`, uno de los directorios que la [estructura de directorios de Linux (FHS)](../estructura-de-directorios-de-linux-fhs/index.md) reserva para registros del sistema; la tercera busca directorios `node_modules`; la cuarta lista lo modificado en la última semana; la quinta filtra por tamaño; la última combina tipo, nombre y fecha en una sola expresión.

| Prueba u opción | Qué evalúa | Ejemplo |
|---|---|---|
| `-name` / `-iname` | Patrón sobre el nombre del archivo; `-iname` no distingue mayúsculas | `find . -iname "*.pdf"` |
| `-type f, d, l` | Tipo de entrada: archivo, directorio o enlace simbólico | `find . -type d` |
| `-mtime -n / +n` | Días desde la última modificación, con signo | `find . -mtime -7` |
| `-size` | Tamaño con sufijos `c`, `k`, `M`, `G` | `find . -size +100M` |
| `-maxdepth n` | Limita la profundidad del recorrido | `find . -maxdepth 1` |
| `-empty` | Archivos o directorios vacíos | `find . -type f -empty` |
| `-newer archivo` | Más reciente que un archivo de referencia | `find /etc -newer /etc/hostname` |

Por defecto `find` une varias pruebas con un «y» lógico implícito: una entrada tiene que cumplirlas todas para aparecer en el resultado.

## Acciones de find: -exec {} \; frente a {} + y -delete

Encontrar rutas no siempre alcanza: a veces hace falta hacer algo con cada una, y las dos formas de cerrar `-exec` se comportan de manera muy distinta.

**`-exec comando {} \;`** ejecuta el comando **una vez por cada archivo**; `{}` se reemplaza por la ruta, y el punto y coma se escapa con `\` porque, sin escapar, la terminal lo tomaría como el fin de su propia instrucción. Con mil archivos, arranca mil procesos, uno detrás de otro.

**`-exec comando {} +`** agrupa tantos nombres como puede en cada invocación, como `xargs`: resuelve el mismo trabajo con uno o unos pocos procesos. Por eso **conviene siempre que el comando acepte varios argumentos** —`chmod`, `rm`, `grep`—; `{} \;` solo tiene sentido si cada invocación necesita ser independiente.

```bash
# Una invocación por archivo: más procesos, más lento en árboles grandes
find . -name "*.tmp" -exec rm {} \;

# Una sola invocación agrupando varios archivos: más eficiente
find . -name "*.tmp" -exec rm {} +

# Dar permisos de ejecución a todos los scripts .sh de una vez
find . -type f -name "*.sh" -exec chmod +x {} +
```

`find` también trae `-delete`, más directa que `-exec rm {} \;` porque no depende de otro proceso. Antes de borrar de verdad:

- Corré primero la misma expresión con `-print` y revisá la lista completa.
- Restringí ruta y pruebas todo lo posible antes de una acción destructiva.
- `-delete` no pide confirmación ni usa papelera: es irreversible.

## El error más común: espacios, -print0 y xargs -0

Por defecto, `find` imprime una ruta por línea. El problema aparece al enviar ese resultado por una tubería a `xargs` —el comando que convierte líneas de entrada en argumentos de otro comando—, porque sin opciones separa los argumentos por espacios y saltos de línea. Un archivo llamado `informe final.txt` se parte en dos argumentos, `informe` y `final.txt`, y el comando siguiente recibe dos rutas inexistentes en vez de una.

```bash
# Se rompe si algún nombre tiene espacios
find . -name "*.txt" | xargs grep -l "REVISAR"

# Sobrevive a espacios, saltos de línea y comillas en el nombre
find . -name "*.txt" -print0 | xargs -0 grep -l "REVISAR"
```

La solución es el par **`-print0`** y **`xargs -0`**. `-print0` le dice a `find` que termine cada ruta con un **byte nulo** en lugar de un salto de línea; `xargs -0` interpreta la entrada de esa misma manera. Como un byte nulo nunca forma parte de un nombre de archivo válido en Linux, sirve como separador inequívoco: no importa si el nombre tiene espacios, tabulaciones o comillas, llega entero al otro lado de la tubería. Es, con diferencia, el error más frecuente al combinar `find` con `grep` a través de `xargs`.

## Cómo buscar texto con grep

Esta sección retoma un aporte de la comunidad: el hilo *«A brief introduction to Linux | Part XI: Expresiones regulares y Metacaracteres»*, de **TerminusRoot** en el foro de Underc0de, ya explicaba con ejemplos reales buena parte de lo que sigue —`-v`, `-n`, `-i`, `-E`, las anclas `^` y `$`, el punto como comodín y el escapado con `\`—. Sigue vigente; esta guía lo reorganiza junto con la parte de `find`, sin hilo equivalente en el foro.

La sintaxis general es `grep [opciones] patrón [archivo...]`: lee cada archivo línea por línea y, por defecto, imprime las líneas donde aparece el patrón.

```bash
grep -n "error" /var/log/syslog
grep -i "error" /var/log/syslog
grep -v "^#" /etc/ssh/sshd_config
grep -rl "REVISAR" ./src
grep -r --include="*.py" "import os" .
```

La primera muestra coincidencias de «error» con número de línea; la segunda hace lo mismo sin distinguir mayúsculas; la tercera invierte la búsqueda y muestra lo que *no* empieza con `#`, útil para revisar la configuración activa de un archivo lleno de comentarios; la cuarta busca de forma recursiva y solo lista los archivos con coincidencias; la última limita esa búsqueda a archivos `.py`.

| Opción | Qué hace | Ejemplo |
|---|---|---|
| `-r` / `-R` | Búsqueda recursiva dentro de directorios | `grep -r "REVISAR" ./src` |
| `-i` | Ignora mayúsculas y minúsculas | `grep -i "error" registro.log` |
| `-n` | Muestra el número de línea de cada coincidencia | `grep -n "error" registro.log` |
| `-l` | Solo lista los nombres de archivo con coincidencias | `grep -rl "REVISAR" ./src` |
| `-v` | Invierte la coincidencia: imprime lo que no matchea | `grep -v "^#" archivo.conf` |
| `-E` | Activa expresiones regulares extendidas (ERE) | `grep -E "fail\|error" registro.log` |
| `-F` | Interpreta el patrón como cadena fija, sin regex | `grep -F "a+b" archivo.txt` |
| `--include=PATRÓN` | Limita la búsqueda recursiva a archivos que matcheen el patrón | `grep -r --include="*.py" "import os" .` |

## BRE, ERE y PCRE: qué cambia de verdad

Una **expresión regular** es un patrón que describe un conjunto de cadenas posibles, no una sola palabra literal. POSIX define dos modos, y GNU `grep` agrega un tercero que ya no es POSIX.

Las **expresiones regulares básicas** (BRE) son el modo por defecto de `grep`. En este modo, metacaracteres como `+`, `?`, `|`, `(` y `)` **no tienen significado especial a menos que los escapes** con una barra invertida: `\+`, `\?`, `\|`, `\(`, `\)`. Las **expresiones regulares extendidas** (ERE, con `-E`) invierten la regla: esos símbolos tienen significado especial de forma directa, y escaparlos se lo quita.

```bash
# BRE (por defecto): la alternancia necesita escape
grep "fail\|error" registro.log

# ERE: la alternancia es directa
grep -E "fail|error" registro.log

# Anclas de comienzo y fin de línea, iguales en BRE y ERE
grep "^root:" /etc/passwd
grep "bash$" /etc/passwd

# BRE: el "?" opcional necesita escape
grep "colou\?r" texto.txt
# ERE: el "?" es directo
grep -E "colou?r" texto.txt

# PCRE, no garantizado en todas las implementaciones de grep
grep -P "\d{3}-\d{4}" contactos.txt
```

Las anclas `^` y `$` y el punto `.` como comodín funcionan igual en los dos modos POSIX; lo que cambia es solo el tratamiento de los metacaracteres de agrupación y repetición.

| Modo | Cómo se activa | Metacaracteres + ? ( ) | Disponibilidad |
|---|---|---|---|
| BRE (básicas) | Modo por defecto de grep | Hay que escaparlos con `\` para que sean especiales | Garantizada por POSIX en cualquier grep |
| ERE (extendidas) | `grep -E` | Tienen significado especial de forma directa | Garantizada por POSIX en cualquier grep |
| PCRE | `grep -P` (GNU grep) | Igual que ERE, más construcciones no POSIX (`\d`, `\w`, lookahead) | No garantizada en todas las implementaciones |

Si un patrón con `|`, `+` o `?` no hace lo que esperás, fijate si estás en BRE sin escapar el símbolo, o si olvidaste `-E`.

## Combinar find y grep con una tubería

La guía de [redirecciones y tuberías en la terminal](../redirecciones-y-tuberias-en-la-terminal/index.md) cubre la mecánica general de conectar la salida de un comando con la entrada del siguiente. Acá importa por qué conviene unir `find` y `grep`: ninguno cubre solo una búsqueda que combine atributos y contenido.

```bash
# Archivos .log que contienen "OutOfMemory"
find . -name "*.log" -print0 | xargs -0 grep -l "OutOfMemory"

# Archivos de /etc modificados después de /etc/hostname, que mencionan PermitRootLogin
find /etc -type f -newer /etc/hostname -print0 | xargs -0 grep -l "PermitRootLogin"

# Scripts Python modificados en los últimos 30 días con una marca REVISAR pendiente
find . -type f -name "*.py" -mtime -30 -print0 | xargs -0 grep -n "REVISAR"

# Alternativa sin xargs: -exec ya pasa el nombre completo, no hace falta -print0
find . -name "*.conf" -exec grep -l "Listen" {} +
```

El último ejemplo importa: con `-exec grep ... {} +`, `find` pasa los nombres directamente al comando, así que el problema de los espacios ni aparece. Estos patrones son los que después vas a encapsular dentro de un [script de Bash](../bash-scripting-desde-cero/index.md) para repetirlos con variables en lugar de reescribirlos cada vez.

## Errores frecuentes

- **Combinar find con xargs sin -print0/-0.** Funciona con nombres simples y se rompe con el primer archivo que tiene un espacio.
- **Elegir {} \; por costumbre en árboles grandes.** Si el comando acepta varios argumentos, {} + hace el mismo trabajo con menos procesos.
- **Usar `|` en grep básico sin -E ni escaparla.** En BRE, `|` es literal hasta que lo escapás o activás -E.
- **Creer que grep -i y find -iname cubren lo mismo.** Uno mira el contenido; el otro, el nombre del archivo.
- **Correr -delete o -exec rm sin probar antes con -print.** Ninguna pide confirmación ni tiene deshacer.
- **Dejar el patrón sin comillas.** La terminal puede expandir comodines antes de que grep o find los reciban.

## Preguntas frecuentes

**¿Cuál es la diferencia entre buscar por metadatos con find y buscar contenido con grep?**
find recorre un árbol de directorios y evalúa los atributos de cada entrada —nombre, tipo, fecha de modificación, tamaño— sin mirar el contenido. grep hace lo opuesto: lee cada archivo línea por línea y compara el texto contra un patrón. Ninguno reemplaza al otro: find filtra por atributos, grep filtra por contenido, y se combinan con una tubería cuando la búsqueda necesita ambas cosas, por ejemplo archivos recientes que además contengan cierta palabra.

**¿Cómo busco archivos por nombre, tipo, fecha o tamaño?**
Con las pruebas de find: -name o -iname por nombre, -type f o -type d por si es archivo o directorio, -mtime -7 por fecha de modificación, y -size +100M por tamaño. Se combinan en una sola expresión: find /var/log -type f -name "*.log" -mtime -7 -size +10M filtra las cuatro cosas a la vez. Con -maxdepth n limitás cuántos niveles recorre antes de evaluar cada prueba.

**¿Cuándo uso -exec {} \; y cuándo -exec {} + o -print0 con xargs -0?**
-exec comando {} \; ejecuta el comando una vez por archivo: más lento en árboles grandes porque arranca un proceso por coincidencia, pero sirve si cada invocación debe ser independiente. -exec comando {} + agrupa varios nombres por invocación y lanza muchos menos procesos, ideal si el comando acepta varios argumentos, como chmod o rm. -print0 con xargs -0 resuelve otro problema: pasar nombres con espacios por una tubería sin que se corten en dos argumentos.

**¿Qué hace cada opción común de grep?**
-r o -R busca recursivo; -i ignora mayúsculas; -n muestra el número de línea; -l solo lista archivos con coincidencias; -v invierte el resultado; -E activa expresiones regulares extendidas; -F trata el patrón como cadena literal; y --include="*.py" limita la búsqueda recursiva a ese patrón de nombre. Se combinan: grep -rn --include="*.py" "import os" . busca recursivo, solo en .py, con número de línea.

**¿Qué es una expresión regular básica frente a una extendida y a PCRE?**
POSIX define dos modos. Las básicas (BRE), modo por defecto de grep, no dan significado especial a +, ?, | ni a los paréntesis salvo que los escapes con \. Las extendidas (ERE), con -E, les dan ese significado de forma directa. GNU grep suma un tercer modo con -P: expresiones compatibles con Perl (PCRE), con construcciones no POSIX como \d o el lookahead, no garantizadas en todas las implementaciones de grep.

**¿Por qué combinar find y grep con una tubería en vez de usar solo uno de los dos?**
Porque cada uno resuelve la mitad del problema: find no mira contenido y grep no filtra por metadatos. Cuando la búsqueda combina ambos criterios, find selecciona las rutas y grep revisa su contenido. La forma segura es find ... -print0 | xargs -0 grep ..., porque el byte nulo evita que un nombre con espacios se parta en dos argumentos, el error más común al combinarlos.

## Fuentes

Aportes de la comunidad y documentación oficial consultados para esta guía. Fecha de consulta: 29 de julio de 2026.

**Aportes de la comunidad Underc0de**

1. **Underc0de, foro.** [A brief introduction to Linux | Part XI: Expresiones regulares y Metacaracteres](https://underc0de.org/foro/gnulinux/a-brief-introduction-to-linux-part-xi-expresiones-regulares-y-metacaracteres/), por *TerminusRoot*, sección GNU/Linux. Origen de la sección de grep y expresiones regulares de esta guía: cubre -v, -n, -i, -E, las anclas ^ y $, el punto como comodín y el escapado con barra invertida, y sigue vigente.

La parte de `find` de esta guía no se apoya en ningún hilo del foro: no existe un aporte previo de la comunidad sobre este comando, así que esa sección es contenido nuevo. La parte VI de la misma serie de TerminusRoot, [«Operaciones de Archivos y Directorios»](https://underc0de.org/foro/gnulinux/a-brief-introduction-to-linux-part-vi-operaciones-de-archivos-y-directorios/), es una lectura relacionada sobre manejo de archivos en la terminal, pero no trata `find`.

**Documentación oficial**

1. **man7.org.** [find(1)](https://man7.org/linux/man-pages/man1/find.1.html). Sintaxis completa de pruebas y acciones de find.
2. **man7.org.** [grep(1)](https://man7.org/linux/man-pages/man1/grep.1.html). Opciones de grep y comportamiento por defecto.
3. **man7.org.** [xargs(1)](https://man7.org/linux/man-pages/man1/xargs.1.html). Comportamiento de -0 y el problema de separación por espacios.
4. **GNU.** [GNU grep Manual](https://www.gnu.org/software/grep/manual/grep.html). Detalle de BRE, ERE y las extensiones PCRE de GNU grep.
5. **GNU.** [GNU Findutils Manual](https://www.gnu.org/software/findutils/manual/find.html). Referencia extendida de find, incluida la diferencia entre -exec {} \; y -exec {} +.
6. **The Open Group.** [POSIX Base Specifications — grep](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/grep.html). Definición estándar del comportamiento de grep.
7. **The Open Group.** [POSIX Base Specifications — find](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/find.html). Definición estándar de find.
8. **The Open Group.** [POSIX Base Specifications — Regular Expressions](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap09.html). Definición formal de BRE y ERE.

## Guías relacionadas

- [Comandos útiles de Linux para el día a día](../comandos-utiles-de-linux/index.md) — find y grep son dos piezas de un conjunto más grande de comandos de uso diario.
- [Redirecciones y tuberías en la terminal](../redirecciones-y-tuberias-en-la-terminal/index.md) — la mecánica general detrás de find ... | xargs ....
- [Bash scripting desde cero](../bash-scripting-desde-cero/index.md) — para encapsular estas búsquedas en scripts reutilizables.
- [Estructura de directorios de Linux (FHS)](../estructura-de-directorios-de-linux-fhs/index.md) — para orientarte en las rutas que usan los ejemplos de find.
- [Índice de guías de Underc0de](../../index.md)

---

Esta guía forma parte de un proyecto de la comunidad Underc0de para organizar conocimiento técnico en español. Fuente canónica: https://underc0de.org/guias/linux/buscar-archivos-y-texto-con-find-y-grep/
