PowerShell y Regex: Una Guía Completa

Entender las expresiones regulares (regex) puede ser difícil para nosotros, los humanos, pero las regex pueden ser una forma increíblemente poderosa de trabajar con cadenas de texto. En este artículo, aprenderás los conceptos básicos de trabajar con PowerShell y Regex.

Obtendrás una introducción a cmdlets útiles como Select-String, aprenderás sobre grupos de captura regex y obtendrás una introducción a diversas técnicas de análisis regex.

Requisitos previos

  • A Windows 7 or later machine running PowerShell 5.1+. This article will be using PowerShell 7.1.0.

Coincidencia de texto simple con Select-String

Para demostrar PowerShell y regex juntos, siempre es mejor pasar por un ejemplo real.

Digamos que estás recopilando datos de máquinas antiguas sobre su hardware y, utilizando la utilidad wmic, construyes un archivo de texto simple como el siguiente. Lo llamaremos nombredecomputadora.txt.

BiosCharacteristics={7,11,12,15,16,19,20,21,22,23,24,25,27,30,32,33,39,40,42,43}
 BIOSVersion={"ACRSYS - 2","V1.15","INSYDE Corp. - 59040115"}
 BuildNumber=
 Caption=V1.15
 CodeSet=
 CurrentLanguage=
 Description=V1.15
 EmbeddedControllerMajorVersion=1
 EmbeddedControllerMinorVersion=15
 IdentificationCode=
 InstallableLanguages=
 InstallDate=
 LanguageEdition=
 ListOfLanguages=
 Manufacturer=Insyde Corp.
 Name=V1.15
 OtherTargetOS=
 PrimaryBIOS=TRUE
 ReleaseDate=20200826000000.000000+000
 SerialNumber=NXHHYSA4241943017724S00
 SMBIOSBIOSVersion=V1.15
 SMBIOSMajorVersion=3
 SMBIOSMinorVersion=2
 SMBIOSPresent=TRUE
 SoftwareElementID=V1.15
 SoftwareElementState=3
 Status=OK
 SystemBiosMajorVersion=1
 SystemBiosMinorVersion=15
 TargetOperatingSystem=0
 Version=ACRSYS - 2

En este caso, digamos que necesitas extraer el número de serie de esta computadora. Este número de serie se encuentra en la línea SerialNumber=.

En esta situación, Select-String será tu nueva herramienta favorita.

Select-String es un cmdlet de PowerShell que te permite proporcionar un patrón de expresión regular y devolver una cadena que coincida con ese patrón.

Relacionado: Cómo usar Grep de PowerShell (Select-String)

Dado que el patrón que estás buscando se encuentra en un archivo, primero necesitarás leer ese archivo y luego buscar una coincidencia con una expresión regular. Para hacer eso, proporciona un patrón de regex usando el parámetro Pattern y la ruta al archivo de texto usando el parámetro Path.

Select-String -Pattern "SerialNumber" -Path '.\computername.txt'

El cmdlet Select-String lee el archivo .\computername.txt e intenta encontrar un conjunto de caracteres que coincida con SerialNumber.

PowerShell and Regex : an example output of select-string

Aunque no lo creas, ya estás utilizando Regex. Regex, en su forma más simple, es hacer coincidir caracteres específicos. En esta situación, estás haciendo coincidir la frase literal “SerialNumber”.

Sin embargo, es probable que no quieras toda esa línea. En su lugar, vamos a construir un script nativo para obtener solamente los datos que te interesan.

La salida de Select-String es un objeto enriquecido

En el ejemplo anterior, Select-String devolvió lo que parecía ser una simple cadena, pero en realidad esa salida era mucho más. Select-String no solo devuelve una coincidencia de texto, sino que en realidad devuelve un objeto completo.

Por ejemplo, si especificas un patrón de regex de This (is) para buscar en la cadena This is a string, puedes ver que si pasas esa salida a Get-Member, Select-String devuelve un objeto Microsoft.PowerShell.Commands.MatchInfo.

select-string "This (is)" -inputobject "This is a String" | get-member
The properties of a select-string operation

Uso de grupos de captura

En el ejemplo anterior, fíjate en el patrón de regex utilizado (This (is)). Este patrón contiene un conjunto de paréntesis. En una expresión regular, esos paréntesis crean un grupo de captura.

Al rodear un término de búsqueda con paréntesis, PowerShell crea un grupo de captura. Los grupos de captura “capturan” el contenido de una búsqueda de regex en una variable.

Observa en el ejemplo anterior que Select-String devuelve una propiedad llamada Matches. Esta propiedad contiene todas las líneas o valores de los grupos de captura (si se usan paréntesis) encontrados.

Los valores de todos los grupos de captura se encuentran bajo la propiedad Matches.Groups. La propiedad groups es un array de objetos, dentro del cual la propiedad value es el dato real. El array groups comienza desde 0 (con un valor de toda la coincidencia de regex) e incrementa por cada grupo de captura que especifiques en el término de regex.

Para el ejemplo anterior, puedes extraer tanto la cadena completa con la propiedad matches, como la coincidencia is que extrajiste:

$match = select-string "This (is)" -inputobject "This is a String"
#esta propiedad coincidirá con todo el valor de select-string
$match.Matches.groups[0].value
#esta propiedad coincidirá con el primer grupo de captura
$match.Matches.groups[1].value
the output of a capture group

Usando Grupos de Captura con Coincidencias de Patrones

Capturar una cadena literal es bastante inútil, como capturar la literal is en This is. No estás obteniendo datos valiosos al capturar una cadena cuyo contenido ya conocías. También puedes combinar grupos de captura con coincidencias de patrones para extraer solo la información que te interesa.

La coincidencia de patrones implica el uso de caracteres especialmente definidos para coincidir con un rango de caracteres en lugar de uno específico. Puedes pensar en la coincidencia de patrones como un comodín * (como en el bloc de notas) en esteroides.

Supongamos que deseas coincidir solo con el número de serie en la línea SerialNumber=NXHHYSA4241943017724S00 y no con toda la línea. Te gustaría capturar cualquier carácter después de la frase SerialNumber=. Puedes extraer ese patrón utilizando el punto especial ., seguido de un comodín de expresión regular * (llamado cuantificador).

El punto le indica a la expresión regular que coincida con cualquier carácter único después de SerialNumber=. El * le dice a la expresión regular que repita la coincidencia del . cero o más veces. Combinado con un grupo de captura, la expresión regular se verá así: SerialNumber=(.*). Puedes verlo a continuación:

$string = "SerialNumber=numberwecareabout1042"
#extraer el número de serie usando un grupo de captura
$match = select-string "SerialNumber=(.*)" -inputobject $string
#imprimir el número de serie
$match.matches.groups[1].value
Using Capture Groups to extract important information

El caracter especial . es solo una de las muchas posibilidades de coincidencia de patrones. Puedes coincidir con palabras, rangos de caracteres, rangos de números, y más. La categoría de Referencia Regex en el sitio web de regexr (a través de la barra lateral) es un excelente recurso para diferentes expresiones regulares.

A Practical PowerShell Regex Example

Juntando todo lo anterior, creemos un script que:

  1. Recibe una lista de archivos de texto (en el ejemplo, solo tomarás el archivo de texto de muestra)
  2. Bucle a través de los archivos de texto y encuentra el número de serie usando SerialNumber=(.*)
  3. Genera una tabla hash que tiene una lista de nombres de computadoras y sus números de serie asociados
#Crea una tabla hash para contener los números de serie
$serialNumbers = @{}

#Obtén todos los archivos de texto. En este caso, estás limitando tu alcance a un solo archivo de texto
$files = Get-ChildItem "$pwd\computername.txt"

#popula la tabla hash
foreach ($file in $files) {
    #primero, recupera la misma cadena, como en el primer ejemplo. Esta vez, también captura la información después de la etiqueta en un grupo de captura
    $serialNumber = select-string "SerialNumber=(.*)" $file.FullName
    #ahora, usa el grupo de captura para extraer solo el número de serie. Esto se hace usando la propiedad de coincidencias especiales. También usamos el nombre de archivo (sin extensión) como índice para el número de serie
    $serialNumbers[$file.basename] = $serialNumber.matches.groups[1].value
}
#escribe la salida de la tabla hash en la pantalla
$serialNumbers | format-table

Puedes ver el script anterior en acción a continuación usando computername.txt:

output of the above code

El Operador Match

Has aprendido cómo usar Select-String para hacer coincidir patrones regex en texto pero PowerShell también tiene algunos operadores útiles que admiten regex.

Uno de los operadores regex de PowerShell más útiles y populares es el operador match y notmatch. Estos operadores te permiten probar si una cadena contiene o no un patrón regex específico.

Si la cadena does coincide con el patrón, el operador match devolverá un valor Verdadero. Si no, devolverá un valor Falso. Lo contrario es cierto para el operador notmatch.

A continuación verás un ejemplo simple de este comportamiento en acción.

#ejemplo de uso de un parámetro de coincidencia
if("my string" -match "string") {
    "string is in my string!"
}

El Operador Split

Si deseas dividir cadenas en un carácter no estático como un espacio, una coma o una tabulación, puedes usar el operador split. El operador split realiza una coincidencia de regex en una cadena y toma una segunda acción de dividir la cadena en una o más cadenas.

El operador split “convierte” una cadena en una matriz de cadenas divididas en un patrón regex específico.

#crear una matriz de cadenas divididas por el símbolo "\". El "\" se escapa dentro de split porque es un carácter especial
"somebody\once told me\the world\is going\to roll me" -split ("\\")

Validación de parámetros del parámetro ValidatePattern

El soporte de regex de PowerShell no se limita solo a los cmdlets y operadores; también puedes integrar la coincidencia de regex en los parámetros.

Relacionado: Todo lo que siempre quisiste saber sobre los parámetros de PowerShell

Usando el atributo de validación del parámetro ValidatePattern, puedes validar valores de parámetros de cadena basados en un patrón regex. Esta rutina de validación es útil para limitar qué entrada puede usar un usuario para un valor de parámetro.

#ejemplo de validación utilizando regex. La función ValidatePattern en esta función solo aceptará letras alfabéticas en minúsculas o mayúsculas, así como espacios.
#el ^ al inicio del regex representa el inicio de la cadena, y $ al final
#representa el final de la cadena (para que coincida con toda la cadena). El +
#significa que la cadena debe tener uno o más caracteres para ser aceptada
#esto tendrá éxito
function alphaOnly {
    param([ValidatePattern('^[a-zA-Z ]+$')][string]$alphaCharacters)
    write-output $alphaCharacters
}
#esto fallará
alphaOnly "Hi Mom"
#esto fallará
alphaOnly "Hi Mom!"

Reemplazar texto con PowerShell y Regex

En las secciones anteriores, aprendiste algunas formas diferentes de hacer coincidir patrones con PowerShell y regex. Puedes llevar ese conocimiento un paso más allá y también reemplazar el texto que PowerShell ha encontrado.

Un método popular para reemplazar texto con regex es utilizar el operador -replace. El operador -replace toma dos argumentos (separados por una coma) y te permite usar regex para reemplazar una cadena con otra. -replace también admite grupos de captura, lo que te permite hacer coincidir un grupo de captura en la búsqueda y utilizar la coincidencia en el reemplazo.

Por ejemplo, utilizando -replace puedes agregar texto a un número de serie:

$string = "SerialNumber=numberwecareabout1042"
$currentYear = "2020"
#agregar el año al final del número de serie
$serialNumber = $string -replace "SerialNumber=(.*)","SerialNumber=`$1-$currentYear"
write-output $serialNumber
Appending text using the -replace operator and capture groups

Ten en cuenta que en el ejemplo anterior, el signo de dólar en $1 está escapado con una comilla invertida. De lo contrario, PowerShell trataría ese $1 como una variable en lugar de un carácter especial de regex.

Aprendiendo Cómo Escribir Mejores Expresiones Regulares en PowerShell

Todo lo anterior puede sonar complicado y, bueno, lo es. De hecho, hay muchas características de regex que no se cubren en el ejemplo anterior. Afortunadamente, regex es un método ampliamente utilizado para la lectura automática, y hay toneladas de utilidades para aprender a usar regex de manera efectiva.

  • RegexOne es considerado el recurso por excelencia para aprender regex. RegexOne presenta las capacidades de regex de manera concisa e interactiva, permitiéndote aprender regex mientras lo escribes. RegexOne es un recurso fantástico para comenzar a aprender cómo funciona regex desde el principio
  • Regexr es una de las mejores herramientas disponibles para validar y construir tus expresiones regulares. Además de tener una excelente herramienta de prueba de regex en tiempo real, regexr también incluye una hoja de trucos y un fantástico motor de documentación.
  • Regexstorm utiliza específicamente el motor .Net para impulsar su herramienta. El sitio no tiene todas las campanas y silbatos que sitios como Regexr tienen, pero va a probar con precisión tu expresión regular de la misma manera que PowerShell lo hace. Incluso si usas otras herramientas para construir tu regex, siempre debes ejecutar el regex a través de regexstorm para asegurarte de que PowerShell lo interprete correctamente.

¡No uses PowerShell y Regex si no es necesario!

PowerShell trabaja con objetos. PowerShell está construido alrededor de objetos estructurados. Los objetos con propiedades son mucho más fáciles de manejar que el texto suelto donde entra en juego Regex.

Relacionado: Volver a lo básico: comprender los objetos de PowerShell

Uno de los principales propósitos de PowerShell y también de lenguajes estructurados como JSON es hacer que Regex y el análisis de texto sean obsoletos. El lenguaje humano es fantástico para que Regex lo decodifique, pero en general, Regex es algo que intentas evitar cuando se trata de almacenar o transferir datos.

Relacionado: Domando APIs REST con PowerShell y JSON

Incluso algunas personas se alteran por el uso de Regex en lenguajes estructurados.

Si puedes utilizar un método orientado a objetos o un lenguaje estructurado como JSON, XML, etc. en lugar de Regex, ¡hazlo! Aunque puedas hacer casi cualquier cosa con Regex, ¡no significa que debas hacerlo!

Continuando con Regex

Con este artículo, ahora deberías tener una comprensión básica de cómo Regex ayuda a las máquinas a analizar y encontrar texto, incluso al buscar frases altamente específicas o complicadas. También deberías tener las herramientas para probar, validar y aprender sobre Regex en el contexto de PowerShell.

Si aún no lo has hecho, los tutoriales de RegexOne son un siguiente paso fantástico. ¡Prueba tus conocimientos de regex y potencia tus habilidades de manipulación de cadenas en PowerShell!

Source:
https://adamtheautomator.com/powershell-regex/