¿Qué son las expresiones regulares?

También llamadas Regex, son patrones de texto que defines para buscar documentos y encontrar exactamente lo que deseas.

Sirven para ser mejor desarrollador y su manejo es una habilidad fundamental en escenarios reales y de CTF.

Se puede usar https://regexr.com/ para las prácticas.

Charsets o sets de caracteres

Cuando busquemos una cadena específica en un archivo o bloque de texto, puedes buscar tal cual con:

grep 'cadena' <archivo>

Pero ¿qué pasa cuando buscas patrones de texto? Por ejemplo, podrías estar buscando una palabra que empiece por una letra específica, o cualquier palabra que termine en un número.

Ahí es donde entran las expresiones regulares.

Ambos problemas pueden resolverse usando charsets. Un charset o set de caracteres se define con corchetes []. Entre ellos pones los caracteres, o rango de caracteres, que deseas encontrar.

Así encontrarás cada ocurrencia del patrón que hayas definido en el texto/archivo que estés buscando.

  • [abc] encontrará a,b y c, cada ocurrencia de cada letra.

  • [abc]zz encontrará azz, bzz y czz.

También podemos definir rangos con un guion -.

  • [a-c]zz es lo mismo que hemos visto justo antes.

Y puedes combinar rangos dentro de un mismo corchete.

  • [a-cx-z]zz encontrará azz, bzz, czz, xzz, yzz y zzz.

De manera habitual, se puede usar para encontrar cualquier caracter alfabético.

  • [a-zA-Z] encontrará cada letra individual en minúscula y en mayúscula.

Se puede usar para nombres también.

  • file[1-3] encontrará file1, file2 y file3.

Hay maneras de excluir caracteres de un grupo con ^ e incluir todo lo demás.

  • [^k]ing enontrará ring, sing o incluso $ing, pero no king.

Por supuesto, puedes excluir charsets, no solo caracteres individuales.

  • [^a-c]at encontrará fat y hat, pero no bat o cat.

Importante: no hay que confundir cadenas con set de caracteres. El set [abc] encontrará la cadena abc, pero también cba y ca. No encuentra la cadena, sino cada ocurrencia de los caracteres especificados en esa cadena.

Cuando especifiquemos charsets, deberíamos escribir las letras en el mismo orden en el que aparecen en las preguntas que vamos a ver, para evitar escribir algo correcto que no es la respuesta adecuada.

Algunas de las respuestas van a ser complicadas. Hay muchos patrones diferentes que pueden encajar con cadenas específicas. Eso significa que has encontrado la solución adecuada que no es esa respuesta exacta.

Dicha respuesta suele ser la expresión regular más eficiente. Eficiente en este contexto significa 2 cosas.

  1. Ser específico. Puedes encontrar cualquier caracter usando [a-z], pero si se pide encontrar desde la a a la c, deberíamos usar [a-c].
  2. No ser demasiado específico. En contraste con lo anterior, si una pregunta requiere encontrar a, c, f, r, s y z, en ese punto, la expresión que encaja con todos sería mucho más larga y complicada que usar [a-z], que es más simple.

Vamos con las preguntas.

Encuentra los siguientes caracteres: c, o, g.

[cog]

Encuentra las siguientes palabras: cat, hat, fat

[cfh]at

Encuentra las siguientes palabras: Cat, cat, Hat, hat

[CcHh]at

Encuentra los siguientes nombres de archivo: File1, File2, file3, file4, file5, File7, file9

[Ff]ile[1-9]

Encuentra lo anterior excepto File7 usando el símbolo ^

[Ff]ile[^7]

Comodines y caracteres opcionales

El comodín es el punto . y sirve para encontrar todos los caracteres excepto el salto de línea.

  • a.c encontrará aac, abc, a0c, a!c y así.

También puedes poner como opcional un caracter en tu patrón usando la ? tras el caracter, lo que significa que abc? encajará con ab y abc, ya que la c es opcional.

Si se quiere buscar un . tienes que escaparlo con \. Eso significa que a.c encajará con a.c, pero también con abc, a!c, etc. Pero a\.c encontrará solamente a.c.

Vamos con las preguntas.

Encaja las siguientes palabras: Cat, fat, hat, rat

.at

Encaja las siguientes palabras: Cat, cats

[Cc]ats?

Encuentra el siguiente nombre de dominio: cat.xyz

cat\.xyz

Encuentra los siguientes nombre de dominio: cat.xyz, cats.xyz, hats.xyz

[ch]ats?\.xyz

Encuentra cada cadena de 4 letras que no acabe en ninguna letra que vaya desde la n a la z

...[^n-z]

Encuentra bat, bats, hat, hats, but not rat or rats (usando ^)

[^r]ats?

Metacaracteres y repeticiones

Hay maneras de encontrar sets de caracteres más grandes. Por ejemplo \d se usa para encajar con cualquier dígito individual. Aquí hay una referencia.

  • \d encuentra un dígito como 9.
  • \D encuentra un no-dígito como A o @.
  • \w encuentra un carácter alfanumérico, como a o 3.
  • \W encuentra un carácter no alfanumérico, como ! o #
  • \s encuentra un espacio en blanco (espacio, tabulador y saltos de línea).
  • \S encuentra todo lo demás (caracteres alfanuméricos y símbolos).

Los guiones bajos _ están incluidos en el metacarácter \w y no en \W. Eso significa que \w encajaría con cada carácter individual en test_file.

A menudo, queremos un patrón que encaje muchos caracteres de un mismo tipo y lo podemos hacer con repeticiones. Por ejemplo, {2} se usa para encajar el carácter precedente (o metacarácter o charset) dos veces consecutivas. Eso significa que z{2} encontrará exactamente zz.

He aquí una referencia para cada repetición, junto con cuántas veces encaja en el patrón precedente.

  • {12} - exactamente 12 veces.
  • {1,5} - de 1 a 5 veces.
  • {2,} - 2 o más veces.
  • * - 0 o más veces.
  • + - 1 o más veces.

Encuentra la siguiente palabra: catssss

cats{4}

Encuentra todas las siguientes palabras (usando el signo *): Cat, cats, catsss.

[Cc]ats*

Encuentra todas las siguientes frases (usando el signo +): regex go br, regex go brrrrrr

regex go br+

Encuentra todos los siguientes nombres de archivo: ab0001, bb0000, abc1000, cba0110, c0000 (sin usar un metacarácter).

[abc]{1,3}[01]{4}

Encuentra los siguientes nombres de archivo: File01, File2, file12, File20, File99

[Ff]ile\d{1,2}

Encuentra todos los siguientes nombres de directorios: kali tools, kali tools

kali\s+tools

Encuentra todos los siguientes nombres de archivo: notes~, stuff@, gtfob#, lmaoo!

\w{5}\W

Encuentra la siguiente cadena entre comillas (usa el signo * y los metacarácteres \s y \S): “2f0h@f0j0%! a)K!F49h!FFOK”

\S*\s*\S*

Encuentra cada cadena de 9 caracteres (con letras, números y símbolos) que no termine en un signo !.

\S{8}[^!]

Encuentra todos estos nombres de archivo (usando el símbolo +): .bash_rc, .unnecessarily_long_filename, y note1

\.?\w+

Comienza/termina con, grupos y cada/o

A veces es muy útil especificar que queremos buscar por un cierto patrón al principio o al final de una línea. Lo hacemos con estos caracteres.

  • ^ comienza con.

  • $ termina con.

Por ejemplo, si queremos encontrar una línea que empieza con abc, usamos ^abc

Si queremos una línea que termina con xyz usamos xyz$.

Importante el símbolo ^ se usa para excluir un carácter cuando está encerrado entre [], pero cuando no, se usa para especificar el comienzo de una palabra.

También puedes definir grupos encerrando un patrón dentro de paréntesis (). Esta función puede ser usada de muchas formas, pero aquí la usaremos para definir un patrón este/o y también para repetir patrones.

  • Para decir “o” en regex usamos “|”.

Ejemplo de este/o es el patrón «durante (el|la) (día|noche)», que encontrará ambas expresiones: «durante el día» y «durante la noche».

Para un ejemplo de repetición, el patrón (no){5} encontrará nonononono.

Encuentra cada cadena que comienza con “Password:“, seguido de 10 caracteres cualquiera excluyendo el 0.

^Password:[^0]{10}

Encuentra “username: “ al principio de una línea (ojo al espacio).

^username:\s

Encuentra cada línea que no empiece con un dígito (usa un metacarácter).

^\D

Encuentra esta cadena al final de una línea:EOF$

EOF\$$

Encuentra todas las siguientes frases.

  • I use nano.
  • I use vim.

I use (nano|vim)

Encuentra todas las líneas que empiezan por $, seguidas de un único dígito, seguido por $, seguido por uno o más caracteres que no sean espacio en blanco.

^\$\d\$\S+

Encuentra cada direccion IP v4 posible (usando metacaracteres y grupos)

(\d{1,3}\.){3}\d{1,3}

Encuentra todos estos emails añadiendo también el nombre de usuario y el nombre de dominio (no el TLD) en grupos separados (usa \w) hello@tryhackme.com, username@domain.com, dummy_email@xyz.com

(\w+)@(\w+)\.com