GNU/Linux >> Tutoriales Linux >  >> Linux

Regex y grep:Flujo de datos y bloques de construcción

En Introducción a las expresiones regulares , cubrí qué son y por qué son útiles. Ahora echemos un vistazo más profundo a cómo se crean. Porque GNU grep es una de las herramientas que más uso (que proporciona una implementación más o menos estandarizada de expresiones regulares), usaré ese conjunto de expresiones como base para este artículo. Luego veremos sed (otra herramienta que utiliza expresiones regulares) en un artículo posterior.

Todas las implementaciones de expresiones regulares se basan en líneas. Un patrón creado por una combinación de una o más expresiones se compara con cada línea de un flujo de datos. Cuando se hace una coincidencia, se realiza una acción en esa línea según lo prescrito por la herramienta que se utiliza.

Por ejemplo, cuando se produce una coincidencia de patrón con grep , la acción habitual es pasar esa línea a STDOUT y descartar las líneas que no coincidan con el patrón. Como vimos en Primeros pasos con las expresiones regulares:un ejemplo , el -v La opción invierte esas acciones, de modo que las líneas con coincidencias se descartan.

Cada línea del flujo de datos se evalúa por sí sola. Piense en cada línea de flujo de datos como un registro, donde las herramientas que usan expresiones regulares procesan un registro a la vez. Cuando se realiza una coincidencia, se realiza una acción definida por la herramienta en uso en la línea que contiene la cadena coincidente.

Bloques de construcción de expresiones regulares

La siguiente tabla contiene una lista de las expresiones y metacaracteres básicos del bloque de construcción implementados por GNU grep comando (y la mayoría de las otras implementaciones de expresiones regulares), y sus descripciones. Cuando se usa en un patrón, cada una de estas expresiones o metacaracteres coincide con un solo carácter en el flujo de datos que se analiza:

Exploremos estos componentes básicos antes de continuar con algunos de los modificadores. El archivo de texto que usaremos para el Experimento 3 es de un proyecto de laboratorio que creé para una antigua clase de Linux que solía enseñar. Originalmente estaba en un archivo odt de LibreOffice Writer pero lo guardé en un archivo de texto ASCII. Se eliminó la mayor parte del formato de cosas como tablas, pero el resultado es un archivo de texto ASCII largo que podemos usar para esta serie de experimentos.

Ejemplo:entradas de TOC

Echemos un vistazo a un ejemplo para explorar lo que acabamos de aprender. Primero, haz el ~/testing directorio de su PWD (créelo si aún no lo hizo en el artículo anterior de esta serie) y luego descargue el archivo de muestra de GitHub.

[student@studentvm1 testing]$  wget https://raw.githubusercontent.com/opensourceway/reg-ex-examples/master/Experiment_6-3.txt

Para comenzar, usa less comando para mirar y explorar el Experiment_6-3.txt archivo durante unos minutos para tener una idea de su contenido.

Ahora, usemos algo de grep simple expresiones para extraer líneas del flujo de datos de entrada. La Tabla de contenido (TOC) contiene una lista de proyectos y sus respectivos números de página en el documento PDF. Extraigamos el TOC comenzando con líneas que terminan en dos dígitos:

[student@studentvm1 testing]$  grep [0-9][0-9]$ Experiment_6-3.txt

Este comando no es realmente lo que queremos. Muestra todas las líneas que terminan en dos dígitos y omite las entradas de TOC con un solo dígito. Veremos cómo manejar una expresión para uno o más dígitos en un experimento posterior. Mirando el archivo completo en less , podríamos hacer algo como esto.

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[0-9]$"

Este comando está mucho más cerca de lo que queremos, pero no está del todo allí. Obtenemos algunas líneas más adelante en el documento que también coinciden con estas expresiones. Si estudia las líneas adicionales y las observa en el documento completo, puede ver por qué coinciden sin ser parte del TOC.

Este comando también pasa por alto las entradas de la tabla de contenido que no comienzan con "Proyecto de laboratorio". A veces, este resultado es lo mejor que puede hacer, y le da una mejor visión de la TOC que la que teníamos antes. Veremos cómo combinar estos dos grep instancias en una sola en un experimento posterior.

Ahora, modifiquemos un poco este comando y usemos la expresión POSIX. Tenga en cuenta las llaves dobles ([[]] ) a su alrededor:

[student@studentvm1 testing]$ grep "^Lab Project" Experiment_6-3.txt | grep "[[:digit:]]$"

Las llaves individuales generan un mensaje de error.

Este comando da los mismos resultados que el intento anterior.

Ejemplo:systemd

Busquemos algo diferente en el mismo archivo:

[student@studentvm1 testing]$ grep systemd Experiment_6-3.txt

Este comando enumera todas las apariciones de "systemd" en el archivo. Intenta usar -i opción para asegurarse de obtener todas las instancias, incluidas las que comienzan con letras mayúsculas (la forma oficial de "systemd" está en minúsculas). O bien, podría cambiar la expresión literal a Systemd .

Cuente el número de líneas que contienen la cadena systemd . Siempre uso -i para asegurarse de que se encuentren todas las instancias de la expresión de búsqueda independientemente de las mayúsculas y minúsculas:

[student@studentvm1 testing]$ grep -i systemd Experiment_6-3.txt | wc
20      478     3098

Como puedes ver, tengo 20 líneas y deberías tener el mismo número.

Ejemplo:Metacaracteres

Este es un ejemplo de coincidencia de un metacarácter:el corchete izquierdo ([ ). Primero, intentemos sin hacer nada especial:

[student@studentvm1 testing]$  **grep -i "[" Experiment_6-3.txt**
grep: Invalid regular expression

Este error ocurre porque [ se interpreta como un metacarácter. Tenemos que escapar este carácter con una barra invertida (\ ) para que se interprete como un carácter literal y no como un metacarácter:

[student@studentvm1 testing]$ grep -i "\[" Experiment_6-3.txt

La mayoría de los metacaracteres pierden su significado especial cuando se usan dentro de expresiones entre paréntesis:

  • Para incluir un ] literal , colóquelo primero en la lista.
  • Para incluir un ^ literal , colóquelo en cualquier lugar menos primero.
  • Para incluir un [ literal , colóquelo en último lugar.

Repetición

Las expresiones regulares se pueden modificar mediante operadores que le permiten especificar cero, una o más repeticiones de un carácter o expresión. Estos operadores de repetición se colocan inmediatamente después del carácter literal o metacarácter utilizado en el patrón:

Expresión Descripción

Caracteres alfanuméricos

Literales

A-Z,a-z,0-9

Todos los caracteres alfanuméricos y algunos de puntuación se consideran literales. Así la letra a en una expresión regular siempre coincidirá con la letra "a" en el flujo de datos que se está analizando. No hay ambigüedad para estos caracteres. Cada carácter literal coincide con uno y solo un carácter.
. (punto) El metacarácter de punto (.) es la forma de expresión más básica. Coincide con cualquier carácter individual en la posición en la que se encuentra en un patrón. Entonces el patrón b.g coincidiría con "grande", "más grande", "bolso", "baguette" y "pantano", pero no con "perro", "blog", "abrazo", "retraso", "mordaza", "pierna", etc. .

Expresión de paréntesis

[lista de personajes]

GNU grep llama a esto una expresión de paréntesis, y es lo mismo que un conjunto para el shell Bash. Los corchetes encierran una lista de caracteres para hacer coincidir una sola ubicación de carácter en el patrón. [abcdABCD] coincide con las letras "a", b", "c" o "d" en mayúsculas o minúsculas. [a-dA-D] especifica un rango de caracteres que crea la misma coincidencia. [a-zA-Z] coincide con el alfabeto en mayúsculas y minúsculas.

[:nombre de la clase:]

Clases de personajes

Este es un intento POSIX de estandarización de expresiones regulares. Se supone que los nombres de las clases son obvios. Por ejemplo, el [:alnum:] clase coincide con todos los caracteres alfanuméricos. Otras clases son [:digit :] que coincide con cualquier dígito 0-9, [:alpha:] ,[:space:] , y así. Tenga en cuenta que puede haber problemas debido a las diferencias en las secuencias de clasificación en diferentes lugares. Lee el grep página man para más detalles.

^ y $

Anclajes

Estos dos metacaracteres coinciden con el principio y el final de una línea, respectivamente. Se dice que anclan el resto del patrón al principio o al final de una línea. La expresión ^b.g solo coincidiría con "grande", "más grande", "bolsa", etc., como se muestra arriba, si aparecen al principio de la línea que se está analizando. El patrón b.g$ coincidiría con "grande" o "bolsa" solo si aparecen al final de la línea, pero no con "más grande".

Como ejemplo, ejecute cada uno de los siguientes comandos y examine los resultados cuidadosamente, para que comprenda lo que está sucediendo:

[student@studentvm1 testing]$  **grep -E files? Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives*" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives+" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{,2}" Experiment_6-3.txt**
[student@studentvm1 testing]$  **grep -Ei "drives{2,3}" Experiment_6-3.txt**

Asegúrese de experimentar con estos modificadores en otro texto del archivo de muestra.

Modificadores de metacaracteres

Todavía hay algunos modificadores interesantes e importantes que necesitamos explorar:

Operador Descripción
?

En expresiones regulares el ? significa cero o una aparición como máximo del carácter anterior. Entonces, por ejemplo, drives? coincide con "unidad" y "unidades", pero no con "conductor". Este resultado es un poco diferente del comportamiento de ? en un globo.

* El carácter que precede al * se igualará cero o más veces sin límite. En este ejemplo, drives* coincide con "drive", "drives" y "drivesss", pero no con "driver". Nuevamente, esto es un poco diferente del comportamiento de * en un globo.
+ El carácter que precede al + coincidirá una o más veces. El carácter debe existir en la línea al menos una vez para que se produzca una coincidencia. Como ejemplo, drives+ coincide con "drives" y "drivesss" pero no con "drive" o "driver".
{n} Este operador coincide con el carácter anterior exactamente n veces. La expresión drives{2} coincide con "drivess" pero no con "drive", "drives", "drivesss" o cualquier número de caracteres "s" finales. Sin embargo, debido a que "drivesssss" contiene la cadena drivess , se produce una coincidencia en esa cadena, por lo que la línea sería una coincidencia de grep .
{n,} Este operador coincide con el carácter anterior n o más veces. La expresión drives{2,} coincide con "drivess" pero no con "drive", "drives", "drivess", "drives" o cualquier número de caracteres "s" finales. Porque "drivesssss" contiene la cadena drivess , se produce una coincidencia.
{,m} Este operador coincide con el carácter anterior no más de m veces. La expresión drives{,2} coincide con "drive", "drives" y "drivess", pero no con "drivesss" o cualquier número de caracteres "s" finales. Una vez más, porque "drivesssss" contiene la cadena drivess , se produce una coincidencia.
{n,m} Este operador coincide con el carácter anterior al menos n veces, pero no más de m veces. La expresión drives{1,3} coincide con "drives", "drivess" y "drivesss", pero no "drivessss" o cualquier número de caracteres "s" finales. Una vez más, debido a que "drivesssss" contiene una cadena coincidente, se produce una coincidencia.

Ahora tenemos una forma de especificar los límites de las palabras con \< y \> metacaracteres. Esto significa que ahora podemos ser aún más explícitos con nuestros patrones. También podemos usar la lógica en patrones más complejos.

Como ejemplo, comience con un par de patrones simples. Este primero selecciona todas las instancias de drives pero no drive , drivess , o caracteres "s" finales adicionales:

 [student@studentvm1 testing]$  **grep -Ei "\<drives\>" Experiment_6-3.txt**

Ahora construyamos un patrón de búsqueda para localizar referencias a tar (el comando tape archive) y referencias relacionadas. Las dos primeras iteraciones muestran más que solo tar -líneas relacionadas:

[student@studentvm1 testing]$ grep -Ei "tar" Experiment_6-3.txt
[student@studentvm1 testing]$ grep -Ei "\<tar" Experiment_6-3.txt
[student@studentvm1 testing]$  grep -Ein "\<tar\>" Experiment_6-3.txt

El -n La opción en el último comando anterior muestra los números de línea para cada línea en la que ocurrió una coincidencia. Esta opción puede ayudar a localizar instancias específicas del patrón de búsqueda.

Consejo: Las líneas de datos coincidentes pueden extenderse más allá de una sola pantalla, especialmente cuando se busca un archivo grande. Puede canalizar el flujo de datos resultante a través de la utilidad less y luego usar la función de búsqueda less que también implementa expresiones regulares para resaltar las ocurrencias de coincidencias con el patrón de búsqueda. El argumento de búsqueda en less es:\<tar\> .

El siguiente patrón busca "script de shell", "programa de shell", "variable de shell", "entorno de shell" o "indicador de shell" en nuestro documento de prueba. Los paréntesis alteran el orden lógico en el que se resuelven las comparaciones de patrones:

[student@studentvm1 testing]$ grep -Eni "\<shell (script|program|variable|environment|prompt)" Experiment_6-3.txt

Nota: Este artículo es una versión ligeramente modificada del Capítulo 6 del Volumen 2 de mi libro de Linux, "Uso y administración de Linux:de cero a SysAdmin", que se publicará en Apress a finales de 2019.

Elimina los paréntesis del comando anterior y ejecútalo de nuevo para ver la diferencia.

Resumiendo

Aunque ahora hemos explorado los componentes básicos de las expresiones regulares en grep , hay una variedad infinita de formas en las que se pueden combinar para crear patrones de búsqueda complejos pero elegantes. Sin embargo, grep es una herramienta de búsqueda y no proporciona ninguna capacidad directa para editar o modificar una línea de texto en el flujo de datos cuando se realiza una coincidencia. Para ello, necesitamos una herramienta como sed , que trataré en mi próximo artículo.


Linux
  1. ¿Usar el comando Grep y localizar?

  2. Corte/Grep Y Df -h?

  3. El desafío y la promesa de Big Data

  4. Resumen de CCPA y RGPD

  5. Preguntas frecuentes sobre el disco del sistema y el disco de datos

Grep Regex:una guía completa

Expresiones regulares en Grep (Regex)

KDE Connect está mejorando cada vez más

Cómo conectar y compartir datos entre dos sistemas Linux

Apache Cassandra:características e instalación

10 ejemplos prácticos de expresiones regulares con grep

    Modificador Descripción
    < Esta expresión especial coincide con la cadena vacía al comienzo de una palabra. La expresión <fun coincidiría con "diversión" y "Función", pero no con "reembolso".
    > Esta expresión especial coincide con el espacio normal o la cadena vacía (" ") al final de una palabra, así como con la puntuación que normalmente aparece en la cadena de un solo carácter al final de una palabra. Entonces environment> coincide con "entorno", "entorno" y "entorno", pero no "entornos" o "ambiental".
    ^ En una expresión de clase de caracteres, este operador niega la lista de caracteres. Así, mientras la clase [a-c] coincide con "a", "b" o "c" en esa posición del patrón, la clase [^a-c] coincide con cualquier cosa menos "a", "b" o "c".
    | Cuando se usa en una expresión regular, el | metacarácter es un operador lógico "o". Se llama oficialmente el infijo o alternancia operador. Ya hemos encontrado este en Primeros pasos con expresiones regulares:un ejemplo , donde vimos que la expresión regular "Team|^\s*$" significa, "una línea con 'Equipo' o (| ) una línea vacía que tiene cero, uno o más espacios en blanco, como espacios, tabulaciones y otros caracteres no imprimibles".
    ( and ) Los paréntesis ( and ) nos permite asegurar una secuencia específica de comparación de patrones, como podría usarse para comparaciones lógicas en un lenguaje de programación.