Linux en Español

Tutorial de Regex para ejemplos de Linux (Sed y AWK)

Para trabajar exitosamente con el editor de Linux sed y el comando awk en tus scripts de shell, tienes que entender las expresiones regulares o como también se conocen regex. Ya que hay muchos motores de regex, en este Tutorial de Regex utilizaremos el Shell para ver el poder del bash trabajando con regex.

Primero, necesitamos entender que es regex y como lo utilizaremos.


 

 

¿Qué es Regex?

Para algunas personas, cuando ven expresiones regulares por primera vez dicen que “¡qué clase de vomito ASCII es este!”

Bueno, una expresión regular o regex, en general, es un patrón de texto que defines para que un programa como sed o awk lo utilice para filtrar texto.

Hemos visto alguno de estos patrones cuando introducimos comandos básicos de Linux y, además, vemos como el comando ls utiliza caracteres comodines para filtrar la salida.

 

Tipos de Regex

Existen muchas aplicaciones diferentes que utilizan distintos tipos de regex en Linux, como el regex incluido en los lenguajes de programación (Java, Perl, Python…) y los programas de Linux como (sed, awk, grep) y muchas otras aplicaciones.

Un patrón regex utiliza un motor de expresiones regulares que se encarga de traducir el patrón a información entendible por la máquina.

Linux tiene dos motores de expresiones regulares:

  • El motor Básico de Expresiones Regulares (BRE) por sus siglas en ingles.
  • El motor Extendido de Expresiones Regulares (ERE) por sus siglas en ingles.

La mayoría de los programas en Linux funcionan bien con las especificaciones del motor BRE, pero algunas herramientas como sed entienden un poco de las reglas del motor BRE.

El motor POSIX ERE viene incluido con algunos lenguajes de programación. Proporciona más patrones como la comparación por dígitos y palabras. El comando awk utiliza el motor ERE para procesar sus patrones de expresiones regulares.

Debido a que hay muchas implementaciones de regex, es difícil escribir patrones que funcionen en todos los motores. Por lo tanto, nos enfocaremos en los más comunes utilizados y demostraremos como usarlos en sed y awk.

 

Definir Patrones BRE

Puedes definir un patrón para coincidir el texto de la siguiente forma:

$ echo "Testing regex using sed" | sed -n '/regex/p'

$ echo "Testing regex using awk" | awk '/regex/{print $0}'

regex tutorial

Podrás notar que al regex no le importa donde ocurre el patrón o cuantas veces aparece en el flujo de datos.

La primera regla a conocer es que los patrones de expresiones regulares son sensibles a las mayúsculas.

$ echo "Welcome to LikeGeeks" | awk '/Geeks/{print $0}'

$ echo "Welcome to Likegeeks" | awk '/Geeks/{print $0}'

regex character case

La primera regex tiene éxito porque la palabra “Geeks” existe en mayúsculas, mientras que la segunda línea falla porque solo utiliza letras minúsculas.

Puedes utilizar espacios o números en tu patrón de la siguiente manera:

$ echo "Testing regex 2 again" | awk '/regex 2/{print $0}'

regex space character

 



Caracteres Especiales

Los patrones de regex pueden utilizar algunos caracteres especiales. No puedes incluirlos en tu patrón ya que si lo haces no obtendrás el resultado esperado.

Los caracteres especiales reconocidos por regex son los siguientes:

.*[]^${}\+?|()

Para obtener el resultado deseado necesitas utilizar un carácter de escape el cual en estos casos es una barra invertida (\).

Por ejemplo, si quieres que el patrón contenga el símbolo ($), debes utilizar el carácter de escape de barra invertida de la siguiente manera:

$ awk '/\$/{print $0}' myfile

regex dollar sign

Si necesitas utilizar la barra invertida (\), debes utilizar los caracteres de escape de la siguiente manera:

$ echo "\ is a special character" | awk '/\\/{print $0}'

regex special character

Aunque la barra inclinada no es un carácter especial, obtendrás un error si lo utilizas directamente.

$ echo "3 / 2" | awk '///{print $0}'

regex slash

Así que necesitas utilizar la siguiente sintaxis:

$ echo "3 / 2" | awk '/\//{print $0}'

regex escape slash

 

Caracteres Ancla

Para localizar el inicio de una línea en un texto, utiliza el carácter de intercalación (^).

Puedes utilizarlo de la así:

$ echo "welcome to likegeeks website" | awk '/^likegeeks/{print $0}'

$ echo "likegeeks website" | awk '/^likegeeks/{print $0}'

regex anchor begin character

El carácter de intercalación (^) coincide con el inicio del texto:

$ awk '/^this/{print $0}' myfile

regex caret anchor

¿Qué sucede si lo utilizas en medio del texto?

$ echo "This ^ caret is printed as it is" | sed -n '/s ^/p'

regex caret character

Se imprime por consola como un carácter normal.

Cuando estés utilizando awk, debes utilizar los caracteres de escape de la siguiente forma:

$ echo "This ^ is a test" | awk '/s \^/{print $0}'

regex escape caret

Todo lo anterior es referente al inicio del texto, ¿Qué sucede si estamos buscando el final?

El signo de ($) se utiliza para representar el fin de línea:

$ echo "Testing regex again" | awk '/again$/{print $0}'

regex end anchor

Puedes utilizar tanto el símbolo de intercalación como el de dólar en la misma línea de la siguiente forma:

$ awk '/^this is a test$/{print $0}' myfile

regex combine anchors

Como puedes ver, imprime solo la línea que coincide con el patrón correspondiente.

Puedes filtrar las líneas en blanco con el siguiente patrón:

$ awk '!/^$/{print $0}' myfile

Aquí introducimos la negación la cual se representa con un símbolo de exclamación (!)

El patrón busca líneas vacías en el texto y al aplicar la negación solo imprimirá las líneas que tengan texto.


El Carácter Punto

Este carácter se puede utilizar para realizar la coincidencia con cualquier carácter excepto el de nueva línea (\n).

Observemos el siguiente ejemplo para entender la idea:

$ awk '/.st/{print $0}' myfile

regex dot character

Podemos observar que en este resultado imprime solo las primeras dos líneas porque estas contienen el patrón st, mientras que la tercera no tiene ese patrón y la cuarta contiene el patrón st al inicio así que tampoco coincide con el nuestro.

 

Clases de Caracteres

Puedes hacer coincidencia de cualquier carácter con el carácter especial punto (.), pero si buscas poder coincidir con un conjunto de caracteres en específico, puedes utilizar una clase de carácter.

Las clases de caracteres permiten buscar la coincidencia en un texto en un conjunto de caracteres, si uno de estos coincide, el patrón coincide.

Las clases de caracteres son definidas utilizando corchetes [] de la siguiente forma:

$ awk '/[oi]th/{print $0}' myfile

regex character classes

Aquí, buscamos los caracteres th que tengan un carácter “o” o un carácter i antes de este.

Esto resulta útil cuando se están buscando palabras que pueden contener mayúsculas o minúsculas y no estás seguro de ello.

$ echo "testing regex" | awk '/[Tt]esting regex/{print $0}'

$ echo "Testing regex" | awk '/[Tt]esting regex/{print $0}'

regex upper and lower case

Por supuesto, no está limitado a caracteres; puedes utilizar números o lo que quieras. Puedes emplearlo como desees siempre y cuando hayas entendido la idea.

 

Negando Clases de Carácter

¿Qué tal si buscamos caracteres que no se encuentren en la clase de carácter?

Para lograr esto, coloca antes del rango de la clase de carácter con un símbolo de intercalación de la siguiente forma:

$ awk '/[^oi]th/{print $0}' myfile

regex negate character classes

Así que en este caso todo es aceptable excepto “o” e “i”

 


Utilizando Rangos

Para especificar rangos de caracteres, puede utilizar el símbolo (-) de la siguiente forma:

$ awk '/[e-p]st/{print $0}' myfile

regex ranges

Este patrón hace coincidencia con todos los caracteres entre e y p seguido de un st como se muestras.

También puedes utilizar rangos para los números:

$ echo "123" | awk '/[0-9][0-9][0-9]/'

$ echo "12a" | awk '/[0-9][0-9][0-9]/'

regex number range

Puedes utilizar rangos múltiples y separados de la siguiente manera:

$ awk '/[a-fm-z]st/{print $0}' myfile

regex non-continuous range

Este patrón significa que desde la a hasta la f y desde la m hasta la z deben aparecer antes del texto st para que coincide.

 

Clases de Caracteres Especiales

La siguiente lista incluye clases de caracteres especiales que puedes utilizar:

[[:alpha:]]                          Patrón para caracteres alfabéticos, ya sean en mayúsculas o minúsculas.

[[:alnum:]]                          Patrón para los caracteres 0–9, A–Z, o a–z.

[[:blank:]]                            Patrón para los espacios y tabulaciones solamente.

[[:digit:]]                              Patrón para los dígitos del 0 al 9.

[[:lower:]]                            Patrón para las letras de a–z en minúsculas solamente.

[[:print:]]                            Patrón para cualquier carácter que se pueda imprimir.

[[:punct:]]                          Patrón para cualquier signo de puntuación.

[[:space:]]                          Patrón para cualquier carácter de espacio en blanco: espacio, tabulación, NL, FF, VT, CR.

[[:upper:]]                          Patrón para cualquier letra de A–Z en mayúsculas solamente.

Puedes utilizar las de la siguiente manera:

$ echo "abc" | awk '/[[:alpha:]]/{print $0}'

$ echo "abc" | awk '/[[:digit:]]/{print $0}'

$ echo "abc123" | awk '/[[:digit:]]/{print $0}'

regex special character classes

 


El Asterisco

El asterisco significa que un carácter debe existir cero o más veces.

$ echo "test" | awk '/tes*t/{print $0}'

$ echo "tessst" | awk '/tes*t/{print $0}'

regex asterisk

Este patrón de símbolos es útil para revisar errores ortográficos o variaciones de idioma.

$ echo "I like green color" | awk '/colou*r/{print $0}'

$ echo "I like green colour " | awk '/colou*r/{print $0}'

regex asterisk example

Aquí en estos ejemplos ya sea que escribas color o colour, va a coincidir, porque el asterisco significa que si el carácter “u” existe cero o muchas dentro del texto.

Para hacer coincidir cualquier número de cualquier tipo de carácter puedes utilizar el punto con el asterisco de la siguiente manera:

$ awk '/this.*test/{print $0}' myfile

regex asterisk with dot

No importa cuantas palabras hay entre las palabras “this” y “test”, cualquier línea que coincide se imprimirá.

Puedes utilizar el carácter asterisco con las clases de caracteres.

$ echo "st" | awk '/s[ae]*t/{print $0}'

$ echo "sat" | awk '/s[ae]*t/{print $0}'

$ echo "set" | awk '/s[ae]*t/{print $0}'

asterisk with character classes

Los tres ejemplos coinciden porque el asterisco significa que encontraras cero o más veces cualquier carácter “a” o “e” y por lo tanto se imprime el texto.

 

Expresiones Regulares Extendidas

A continuación, se muestran algunos patrones que pertenecen a Posix ERE:

El símbolo de interrogación

El símbolo de interrogación significa que el carácter anterior puede existir una vez o ninguna.

$ echo "tet" | awk '/tes?t/{print $0}'

$ echo "test" | awk '/tes?t/{print $0}'

$ echo "tesst" | awk '/tes?t/{print $0}'

regex question mark

El símbolo de interrogación puede ser utilizado en combinación con una clase de carácter:

$ echo "tst" | awk '/t[ae]?st/{print $0}'

$ echo "test" | awk '/t[ae]?st/{print $0}'

$ echo "tast" | awk '/t[ae]?st/{print $0}'

$ echo "taest" | awk '/t[ae]?st/{print $0}'

$ echo "teest" | awk '/t[ae]?st/{print $0}'

regex question mark with character classes

Si cualquiera de los caracteres en la clase existe, el texto coincidirá. En cualquier otro caso falla.

Símbolo de adición

El símbolo de adición significa que el carácter antes de este símbolo debería existir una o más veces, pero al menos de existir una vez para que el texto coincida con el patrón.

$ echo "test" | awk '/te+st/{print $0}'

$ echo "teest" | awk '/te+st/{print $0}'

$ echo "tst" | awk '/te+st/{print $0}'

regex plus sign

Si el carácter “e” no es encontrado el texto fallará.

Se puede utilizar con clases de caracteres de la siguiente forma:

$ echo "tst" | awk '/t[ae]+st/{print $0}'

$ echo "test" | awk '/t[ae]+st/{print $0}'

$ echo "teast" | awk '/t[ae]+st/{print $0}'

$ echo "teeast" | awk '/t[ae]+st/{print $0}'

regex plus sign with character classes

Si cualquier carácter de la clase existe en el texto, este coincidirá.

Llaves

Las llaves te permiten especificar el número de existencia de un patrón, este cuenta con dos formatos:

n: La expresión regular aparecerá exactamente n veces.

n,m: La expresión regular aparecerá al menos n veces, pero no más de m veces.

$ echo "tst" | awk '/te{1}st/{print $0}'

$ echo "test" | awk '/te{1}st/{print $0}'

regex curly braces

En versiones antiguas del awk, debes utilizar la opción –re-interval para el comando awk para poder leer las llaves, en versiones más recientes esto no es necesario.

$ echo "tst" | awk '/te{1,2}st/{print $0}'

$ echo "test" | awk '/te{1,2}st/{print $0}'

$ echo "teest" | awk '/te{1,2}st/{print $0}'

$ echo "teeest" | awk '/te{1,2}st/{print $0}'

regex curly braces interval pattern

Por ejemplo, si el carácter “e” existe uno o dos veces, el texto coincidirá; de otra forma fallará.

También pueden ser utilizadas con clases de caracteres así:

$ echo "tst" | awk  '/t[ae]{1,2}st/{print $0}'

$ echo "test" | awk  '/t[ae]{1,2}st/{print $0}'

$ echo "teest" | awk  '/t[ae]{1,2}st/{print $0}'

$ echo "teeast" | awk  '/t[ae]{1,2}st/{print $0}'

regex interval pattern with character classes

Si hay una o dos instancias de las letras “a” o “e” el texto coincidirá con el patrón, de cualquier otra forma, fallará.

Símbolo de Tubería (pipe)

El símbolo de tubería realiza un OR lógico entre 2 patrones. Si al menos un patrón existe en el texto, este coincidirá, de lo contrario fallará, aquí te mostramos un ejemplo:

$ echo "Testing regex" | awk '/regex|regular expressions/{print $0}'

$ echo "Testing regular expressions" | awk '/regex|regular expressions/{print $0}'

$ echo "This is something else" | awk '/regex|regular expressions/{print $0}'

regex pipe symbol

No escribas espacios entre los patrones y el símbolo de tubería.

 

Agrupando Expresiones

Puedes agrupar expresiones de manera que el motor del regex las considere como una sola expresión.

$ echo "Like" | awk '/Like(Geeks)?/{print $0}'

$ echo "LikeGeeks" | awk '/Like(Geeks)?/{print $0}'

regex grouping expressions

El agrupamiento de “Geeks” hace que el motor del regex trate la expresión como una sola pieza, así que, si “LikeGeeks” o la palabra “Like” existe en el texto, este coincidirá.

 

Ejemplos Prácticos

Hemos visto hasta ahora demostraciones simples del uso de patrones de expresiones regulares, es momento de poner el conocimiento en acción, solo para practicar.

Contando Archivos de un Directorio

Observemos al script de bash que cuenta archivos ejecutables en una carpeta dentro de la PATH variable de ambien.

$ echo $PATH

Para obtener el listado del directorio, debes reemplazar cada espacio con el símbolo de dos puntos (:) con un espacio.

$ echo $PATH | sed 's/:/ /g'

Ahora vamos a iterar a través de cada directorio utilizando un ciclo como el siguiente:

¡¡Excelente!!

Ahora puedes obtener la cantidad de los archivos en cada directorio utilizando el comando ls y guardándolo en una variable.

Podrás notar que algunos directorios no existen, no hay problema.

regex count files

¡¡Genial!! Este es el poder de regex. Estas pocas líneas de código pueden contar todos los archivos en todos los directorios. Por supuesto, hay un comando en Linux que hace esto fácilmente, pero aquí discutimos como utilizar regex para algo que puedas utilizar. A ti se te pueden ocurrir ideas más útiles.

Validar Direcciones de Correos Electrónico

Existen un montón de sitios web que ofrecen patrones regex listos para utilizar en cuanto a validación de correos electrónicos, números de teléfono y mucho más, esto es de utilidad, pero aquí queremos conocer cómo funciona esto.

[email protected]

El nombre de usuario puede contener cualquier carácter alfanumérico, combinado el punto, la barra lateral, el signo de suma y el guion bajo.

El nombre del host del correo puede utilizar cualquier carácter alfanumérico combinado con el punto y el guion bajo.

Para el nombre de usuario, el siguiente patrón se adapta a todos los nombres de usuario existentes:

^([a-zA-Z0-9_\-\.\+]+)@

El signo de suma significa que un carácter o más debe ser seguido de un signo @.

Y para el nombre del host del correo electrónico el patrón debería ser el siguiente:

([a-zA-Z0-9_\-\.]+)

Existen reglas especiales para los TLD (por sus siglas en inglés) o Dominios de alto nivel,estos no pueden tener menos de 2 y más de 5 caracteres. El siguiente patrón es un ejemplo de regex para dominós de alto nivel.

\.([a-zA-Z]{2,5})$

Ahora unimos todo:

^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$

Probemos este regex con un correo electrónico:

$ echo "[email protected]" | awk '/^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/{print $0}'

$ echo "[email protected]" | awk '/^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/{print $0}'

regex validate email

¡Genial! funciona excelente.

Esto es solo el principio del mundo regex el cual nunca termina. Espero que esta publicación te ayudara a entender estos vómitos de ASCII y ahora los utilices más profesionalmente.

Espero que te gustara la publicación.

Gracias.

Mokhtar Ebrahim
Estoy trabajando como administrador de sistemas Linux desde 2010. Soy responsable de mantener, proteger y solucionar problemas de servidores Linux para múltiples clientes de todo el mundo. Me encanta escribir guiones de shell y Python para automatizar mi trabajo.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *