# 103.7 - Realizar búsquedas en archivos de texto usando expresiones regulares

<table data-header-hidden data-full-width="true"><thead><tr><th width="148"></th><th></th></tr></thead><tbody><tr><td><strong>Importancia</strong></td><td>3</td></tr><tr><td><strong>Descripción</strong></td><td>El candidato debe ser capaz de manipular archivos y cadenas de texto usando expresiones regulares. El objetivo incluye la creación de expresiones re\ gulares sencillas que contengan varios elementos de notación así como el uso de herramientas para realizar búsquedas con expresiones regulares dentr\ o de un sistema de archivos o del contenido de un archivo.</td></tr></tbody></table>

#### *Áreas de conocimiento clave:*

* Crear expresiones regulares sencillas que contengan varios elementos de notación.
* Saber diferenciar las expresiones regulares básicas de las extendidas.
* Entender los conceptos de caracteres especiales, clases de caracteres, cuantificadores y anclas.
* Usar herramientas para realizar búsquedas con expresiones regulares dentro de un sistema de archivos o del contenido de un archivo.
* Usar las expresiones regulares para borrar, modificar o reemplazar texto.

### *Contenidos*

#### Regular expressions (REGEX)

Hacen referencia a cadenas de texto, se utilizan dentro del contenido de alguna cosa. Caracteres especiales que nos sirve para definir patrones de cadenas de texto. Es importante por que en linux todo es un fichero y toda la información es texto, saber filtrar y encontrar nos interesa.

```bash
grep ".ancho" el_quijote.txt 
```

Aquí el "." hace referencia a cualquier carácter. Esto es importante por que Linux es *sensitive* case pero cuidado por que al usar con el punto significa sancho, Sancho, pancho, ancho...&#x20;

recuerda que con `grep -i` nos saltamos el case sensitive.

```bash
ls -l | grep sess*
```

Aquí le están entrando datos de entrada y sigue evaluando como cadena de texto, por lo tanto es una expresión regular.

<figure><img src="https://727141126-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGDxv9DabSo9OMw53A4vS%2Fuploads%2FZ0Qy50y0mD8dQtNSrcaV%2Fgrep_01png.png?alt=media&#x26;token=56d2ca81-ff0d-406b-bfc5-2b30c11b4b12" alt=""><figcaption></figcaption></figure>

#### BASIC REGEX

* ^ -> inicio de línea
* $ -> fin de línea
* " \* " -> de 0 a N veces **el carácter anterior**
* " . " -> cualquier carácter n veces
* \[A-B] -> rango de valores

&#x20;Por ejemplo&#x20;

`grep [Ss]ancho mini_quijote.txt` pero es mas fácil hacerlo con `grep -i`

<details>

<summary>Ejemplo de uso</summary>

Vamos a usar un ejemplo, vamos a instalar **squid**, un servidor caché, este tiene un archivo de configuración de 9155 líneas (`wc -l /etc/squid/squid.conf`)&#x20;

Nos interesa ver que líneas no están comentadas.

Se puede usar grep -v para invertir la selección pero quitara todas las lineas con ese carácter, incluso si no son comentarios.&#x20;

```bash
grep -v "^#" sample.conf 
```

Inicio de linea seguido de almohadilla para quitar todas.&#x20;

Al volver a revisar con esto:&#x20;

```bash
grep -vn "^#" /etc/squid/squid.conf | wc -l 
```

Ahora nos sigue mostrando 470 líneas que siguen siendo muchas pero por que aparecen líneas en blanco con espacios vacíos.

```bash
grep -n "^$" sample.conf
```

Esto revisa todas  aquellas cadenas que tienen un inicio de linea seguido de un fin de linea (se puede ver los finales de linea con `cat -e sample.conf`)

```bash
grep -vn "^#" /etc/squid/squid.conf | grep -v "^$" | wc -l 
```

Al contar ahora se queda con 34 líneas pero siguen quedándonos dos que no queremos por que tienen líneas con espacios en blanco (vuelve a revisar con cat -e). Ahora usaremos la tercera expresión regular que es \*  &#x20;

```bash
grep -n "^ *$" sample.conf 
#Cuidado por que las lineas en blanco tambien cumplen este patron al ser de 0 a N veces.
grep -n "^,*$" sampe.conf
```

Con el ".\*" podemos agarrarlo todo, sin filtro.

```bash
grep -n ".*" sample.conf
```

Por lo tanto, para filtrar de verdad todas deberemos:

```bash
grep -vn "^#" /etc/squid/squid.conf | grep -v "^ *$" | wc -l 
```

Con esto ya obtenemos las 32 líneas, sin espacios en blanco.

</details>

<details>

<summary>Diferencias entre REGEX y Fileglobbing con wildcards</summary>

| Característica               | File Globbing con Wildcards                                                     | Expresiones Regulares (Regex)                                                   |
| ---------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| **Uso principal**            | Coincidencia de nombres de archivos y rutas en la terminal.                     | Coincidencia de patrones en textos generales.                                   |
| **Donde se usa**             | Shells como Bash, Zsh, Fish (en `ls`, `cp`, `rm`, etc.).                        | Herramientas como `grep`, `sed`, `awk`, `perl`, `python`, etc.                  |
| **Simplicidad**              | Más simple y directo.                                                           | Más complejo y poderoso.                                                        |
| **Wildcards comunes**        | `*` (cualquier cosa), `?` (un solo carácter), `[abc]` (conjunto de caracteres). | `.*` (cualquier cosa), `\d` (dígito), `[a-z]` (rango), `\b` (borde de palabra). |
| **Jerarquía de directorios** | Entiende `/` y distingue archivos y carpetas.                                   | No entiende jerarquía de archivos.                                              |
| **Expresividad**             | Limitada a patrones simples.                                                    | Puede incluir grupos, repeticiones, lookaheads, etc.                            |
| **Compatibilidad**           | Funciona en shells y scripts de sistema.                                        | Compatible con múltiples lenguajes y herramientas.                              |

***

#### **Orígenes y Evolución**

1. **File Globbing (Wildcards)**
   * Surge en los primeros sistemas Unix en los años 70, en el **shell de Thompson** y el **Bourne shell (`sh`)**.
   * Su objetivo era facilitar la búsqueda y manipulación de archivos en la terminal.
   * Se llama "globbing" porque expande los patrones antes de ejecutarse en un comando.
2. **Expresiones Regulares (Regex)**
   * Fueron inventadas por **Stephen Kleene** en 1951 como parte de la teoría de autómatas.
   * Fueron implementadas en Unix en los años 70 en herramientas como `grep`, `sed` y `awk`.
   * Se popularizaron más con Perl y posteriormente con lenguajes como Python, JavaScript y Java.

***

#### **Ejemplos Comparativos**

| Propósito                                                                       | File Globbing   | Expresión Regular |
| ------------------------------------------------------------------------------- | --------------- | ----------------- |
| Buscar archivos `.txt`                                                          | `*.txt`         | `.*\.txt$`        |
| Buscar archivos que empiecen con "log"                                          | `log*`          | `^log.*`          |
| Buscar archivos con un solo carácter extra (`a.txt`, `b.txt`, pero no `ab.txt`) | `?.txt`         | `^.txt$`          |
| Buscar archivos `file1.txt`, `file2.txt`, `file3.txt`                           | `file[1-3].txt` | `file[1-3]\.txt`  |

</details>

#### EXTENDED REGEX

No todos los comandos aceptan extended regex, uno que sí es `grep` que admite extended **regex** pero usando la opción `-E`

* \+ -> De 1 a N veces el carácter anterior
* ? -> De 0 a 1 vez el carácter anterior

Vamos a ver un ejemplo, partiendo del siguiente archivo:

{% code title="sample.conf" lineNumbers="true" %}

```

,
,,,,,
,,, ,,,,
```

{% endcode %}

`grep -nE "^,*$" sample.conf` -> cumple para las tres líneas ya que es 0 o n.

`grep -nE "^,+$" sample.conf` -> la coma tiene que aparecer 1 a n veces, solo se cumple la 2a y 3a líneas.

`grep -nE "^,?$" sample.conf` -> la coma tiene que aparecer 0 o una vez solo, solo se cumple la primera y segunda línea.

Resumen grep

<figure><img src="https://727141126-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGDxv9DabSo9OMw53A4vS%2Fuploads%2FJs0Itgdv2TG0qfWTweQq%2Fimage.png?alt=media&#x26;token=3a471c40-5243-45fd-b210-1b3b57b49dae" alt=""><figcaption></figcaption></figure>

### Command chaining

{% tabs %}
{% tab title="&& (AND)" %}
El operador `&&` solo ejecutará el segundo comando si el primero tiene éxito. Si el primer comando se ejecuta con un estado de salida **cero (0)**, el segundo comando se ejecutará; de lo contrario, no lo hará.

```bash
mkdir demo && cd demo
```

{% endtab %}

{% tab title="|| (OR)" %}
Este operador se comporta como un `else` en programación. Ejecuta el segundo comando si el primero falla (si su código de salida es distinto de **0**).

```bash
cd demo || mkdir demo
```

{% endtab %}

{% tab title="&" %}
Ejecuta un comando o script en segundo plano, permitiendo que la terminal permanezca disponible para otras tareas.

```bash
sudo apt update -y && sudo apt upgrade &
```

{% endtab %}

{% tab title=";" %}
Ejecuta múltiples comandos de forma **secuencial**, sin importar si los anteriores tuvieron éxito o fallaron.

```bash
sudo apt install neofetch; cd demo; sleep 10
```

{% endtab %}

{% tab title="! (NOT)" %}
El operador `!` **niega** una expresión en un comando. Por ejemplo, para eliminar todos los archivos excepto uno en un directorio:

```bash
cd backups && rm !(backup.tar.gz)
```

{% endtab %}

{% tab title="| (Pipe)" %}
El **pipe** (`|`) toma la salida de un comando y la usa como entrada del siguiente comando.

```bash
ls -l | grep ".txt"
```

{% endtab %}

{% tab title="" %}
**Concatenación**

Permite dividir un comando largo en varias líneas.

```bash
ping -c 1 example.com && \
  wget example.com/index.html || \
  echo "El dominio está caído"
```

{% endtab %}

{% tab title="( )" %}
Agrupa comandos para **forzar un orden de ejecución**.

```bash
cmd1 && cmd2 || cmd3   # cmd3 se ejecuta si cmd1 o cmd2 fallan
(cmd1 && cmd2) || cmd3 # cmd3 solo se ejecuta si ambos cmd1 y cmd2 fallan
```

{% endtab %}

{% tab title="{ }" %}
Agrupa varios comandos para que se ejecuten como **una unidad**, pero **solo si el primer comando tiene éxito**.

```bash
[ -f hello.txt ] && echo "El archivo existe"; echo "Hola"  # "Hola" siempre se imprime
[ -f hello.txt ] && { echo "El archivo existe"; echo "Hola"; }  # Ambos comandos solo se ejecutan si hello.txt existe
```

{% endtab %}
{% endtabs %}
