PowerShell e Regex: Um Guia Abrangente

Entender expressões regulares (regex) pode ser uma dor de cabeça para nós, humanos, compreendermos, mas regex pode ser uma maneira incrivelmente poderosa de lidar com strings. Neste artigo, você vai aprender o básico de como trabalhar com PowerShell e Regex.

Você terá uma introdução a cmdlets úteis como Select-String, aprenderá sobre grupos de captura regex e terá uma introdução a várias técnicas de análise regex.

Pré-requisitos

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

Correspondência de Texto Simples com Select-String

Para demonstrar PowerShell e regex juntos, é sempre melhor percorrer um exemplo real.

Vamos dizer que você está coletando dados de máquinas antigas sobre seu hardware e, usando a ferramenta wmic, você cria um arquivo de texto simples como abaixo. Vamos chamá-lo de computername.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

Neste caso, suponha que você precise extrair o número de série deste computador. Este número de série está na linha SerialNumber=.

Nessa situação, Select-String vai ser sua nova ferramenta favorita.

Select-String é um cmdlet do PowerShell que permite que você forneça um padrão de expressão regular e retorne uma string que corresponda a esse padrão.

Relacionado: Como usar o Grep do PowerShell (Select-String)

Uma vez que o padrão que você está procurando está em um arquivo, primeiro você precisará ler esse arquivo e em seguida procurar por uma correspondência de regex. Para fazer isso, forneça um padrão regex usando o parâmetro Pattern e o caminho para o arquivo de texto usando o parâmetro Path.

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

O cmdlet Select-String lê o arquivo .\computername.txt e tenta encontrar um conjunto de caracteres correspondentes a SerialNumber.

PowerShell and Regex : an example output of select-string

Acredite ou não, você já está usando Regex. Regex, em sua forma mais simples, está correspondendo a caracteres específicos. Nesta situação, você está correspondendo à frase literal “SerialNumber”.

No entanto, você provavelmente não quer toda essa linha. Vamos, em vez disso, começar a construir um script nativo para recuperar apenas os dados que você se preocupa.

A Saída de Select-String é um Objeto Rico

No exemplo anterior, Select-String retornou, o que parecia ser, uma string simples, mas essa saída na verdade era muito mais. Select-String não apenas produz uma correspondência de texto. O cmdlet na verdade retorna um objeto inteiro.

Por exemplo, especifique um padrão regex de This (is) para procurar na string This is a string. Você pode ver abaixo que se você encaminhar essa saída para Get-Member, Select-String retorna um objeto Microsoft.PowerShell.Commands.MatchInfo.

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

Usando Grupos de Captura

No exemplo anterior, observe o padrão regex usado (This (is)). Este padrão contém um conjunto de parênteses. Em uma expressão regular, esses parênteses criam um grupo de captura.

Ao cercar um termo de pesquisa com parênteses, o PowerShell está criando um grupo de captura. Os grupos de captura “capturam” o conteúdo de uma pesquisa regex em uma variável.

Observe no exemplo acima que Select-String gera uma propriedade chamada Matches. Essa propriedade contém todas as linhas ou valores dos grupos de captura (se estiverem entre parênteses) encontrados.

Os valores de todos os grupos de captura são encontrados na propriedade Matches.Groups. A propriedade groups é um array de objetos, no qual a propriedade value contém os dados reais. O array groups começa do zero (com o valor da correspondência regex inteira) e incrementa para cada grupo de captura especificado no termo Regex.

No exemplo acima, você pode extrair tanto a string inteira com a propriedade matches, quanto a correspondência is que você extraiu:

$match = select-string "This (is)" -inputobject "This is a String"
# Esta propriedade corresponderá ao valor completo do Select-String
$match.Matches.groups[0].value
# Esta propriedade corresponderá ao primeiro grupo de captura
$match.Matches.groups[1].value
the output of a capture group

Usando Grupos de Captura com Correspondências de Padrões

Capturar uma string literal é bastante inútil, como capturar o literal is em This is. Você não está obtendo dados valiosos ao capturar uma string cujo conteúdo você já conhecia. Você também pode combinar grupos de captura com correspondência de padrões para extrair apenas as informações que você deseja.

A correspondência de padrões é o uso de caracteres especialmente definidos para corresponder a uma variedade de caracteres, em vez de um caractere específico. Você pode pensar na correspondência de padrões como um caractere curinga * (como no bloco de notas) em esteroides.

Digamos que você queira corresponder apenas ao número de série na linha SerialNumber=NXHHYSA4241943017724S00 e não a linha inteira. Você gostaria de capturar qualquer caractere após a frase SerialNumber=. Você pode extrair esse padrão usando o ponto especial ., seguido de um caractere curinga de regex * (referido como Quantificador).

O ponto diz ao regex para corresponder a qualquer caractere único após SerialNumber=. O * diz ao regex para repetir a correspondência do . zero ou mais vezes. Combinado com um grupo de captura, o regex ficará assim SerialNumber=(.*). Você pode ver isso abaixo:

$string = "SerialNumber=numberwecareabout1042"
#extrair o número de série usando um grupo de captura
$match = select-string "SerialNumber=(.*)" -inputobject $string
#saída do número de série
$match.matches.groups[1].value
Using Capture Groups to extract important information

O caractere especial . é apenas uma das muitas possibilidades diferentes de correspondência de padrões. Você pode corresponder palavras, intervalos de caracteres, intervalos de números e similares. A categoria de Referência Regex no site regexr (via barra lateral) é um excelente recurso para diferentes expressões regex.

A Practical PowerShell Regex Example

Ao reunir todos os elementos acima, vamos criar um script que:

  1. Ingesta uma lista de arquivos de texto (no exemplo, você só vai pegar o arquivo de texto de exemplo)
  2. Loop através dos arquivos de texto e encontre o número de série usando SerialNumber=(.*)
  3. Gera uma tabela de hash que possui uma lista de nomes de computador e seus números de série associados
#Crie uma tabela de hash para armazenar os números de série
$serialNumbers = @{}

#Obtenha todos os arquivos de texto. Neste caso, você está limitando seu escopo a um único arquivo de texto
$files = Get-ChildItem "$pwd\computername.txt"

#popule a tabela de hash
foreach ($file in $files) {
    #primeiro, recupere a mesma string, como no primeiro exemplo. Desta vez, também capture as informações após o rótulo em um grupo de captura
    $serialNumber = select-string "SerialNumber=(.*)" $file.FullName
    #agora, use o grupo de captura para extrair apenas o número de série. Isso é feito usando a propriedade de correspondências especiais. Também usamos o nome do arquivo (sem extensão) como índice para o número de série
    $serialNumbers[$file.basename] = $serialNumber.matches.groups[1].value
}
#escreva a saída da tabela de hash na tela
$serialNumbers | format-table

Você pode ver o script acima em ação abaixo usando computername.txt:

output of the above code

O Operador Match

Você aprendeu como usar Select-String para combinar padrões regex em texto, mas o PowerShell também tem alguns operadores úteis que suportam regex.

Um dos operadores regex do PowerShell mais úteis e populares é o operador match e notmatch. Esses operadores permitem testar se uma string contém ou não um padrão regex específico.

Se a string fizer correspondência com o padrão, o operador match retornará um valor Verdadeiro. Se não, retornará um valor Falso. O oposto é verdadeiro para o operador notmatch.

Abaixo, você verá um exemplo simples desse comportamento em ação.

# Exemplo de uso de um parâmetro de correspondência
if("my string" -match "string") {
    "string is in my string!"
}

O Operador Split

Se desejar dividir strings em um caractere não estático como um espaço, uma vírgula ou uma guia, você pode usar o operador split. O operador split realiza uma correspondência regex em uma string e realiza uma segunda ação de dividir a string em uma ou mais strings.

O operador split “converte” uma string em uma matriz de strings divididas com base em um padrão regex específico.

# criar uma matriz de strings divididas pelo símbolo "\". O "\" é escapado dentro do split porque é um caractere especial
"somebody\once told me\the world\is going\to roll me" -split ("\\")

ValidatePattern Validação de Parâmetro

O suporte regex do PowerShell não se limita apenas a cmdlets e operadores; você também pode integrar correspondência regex em parâmetros também.

Relacionado: Tudo que você sempre quis saber sobre Parâmetros do PowerShell

Usando o atributo de validação de parâmetro ValidatePattern, você pode validar os valores dos parâmetros de string com base em um padrão regex. Essa rotina de validação é útil para limitar o que um usuário pode inserir como valor do parâmetro.

#exemplo de validação usando regex. O ValidatePattern nesta função irá
#aceitar apenas letras alfabéticas minúsculas ou maiúsculas, bem como espaços.
#o ^ no início da regex representa o início da string, e $ no final
#representa o final da string (para combinar a *inteira* string). O +
#significa que a string deve ter um ou mais caracteres para ser aceita
function alphaOnly {
    param([ValidatePattern('^[a-zA-Z ]+$')][string]$alphaCharacters)
    write-output $alphaCharacters
}
#isso terá sucesso
alphaOnly "Hi Mom"
#isso falhará
alphaOnly "Hi Mom!"

Substituindo Texto com PowerShell e Regex

Nas seções anteriores, você aprendeu algumas maneiras diferentes de combinar padrões com PowerShell e regex. Você pode levar esse conhecimento um passo adiante e também substituir texto que o PowerShell encontrou.

Um método popular para substituir texto com regex é usar o operador -replace. O operador -replace recebe dois argumentos (separados por uma vírgula) e permite que você use regex para substituir uma string por outra. -replace também suporta grupos de captura, permitindo que você corresponda a um grupo de captura na pesquisa e use a correspondência na substituição.

Por exemplo, usando -replace, você pode acrescentar texto a um número de série:

$string = "SerialNumber=numberwecareabout1042"
$currentYear = "2020"
#acrescenta o ano ao final do número de série
$serialNumber = $string -replace "SerialNumber=(.*)","SerialNumber=`$1-$currentYear"
write-output $serialNumber
Appending text using the -replace operator and capture groups

Observe que, no exemplo acima, o símbolo do dólar em $1 é escapado usando um acento grave. Caso contrário, o PowerShell trataria o $1 como uma variável em vez de um caractere especial de regex.

Aprendendo a Escrever Melhores Expressões Regulares no PowerShell

Tudo isso pode parecer complicado e, bem, é. Na verdade, existem muitos recursos de regex não abordados no exemplo acima. Felizmente, regex é um método amplamente utilizado de leitura por máquina e existem muitas ferramentas disponíveis para ajudar a aprender como usar efetivamente o regex.

  • RegexOne é considerado o recurso padrão para aprender regex. O RegexOne apresenta as capacidades do regex de forma fácil e interativa, permitindo que você aprenda regex enquanto o escreve. O RegexOne é um recurso fantástico para começar a aprender como o regex funciona desde o início.
  • Regexr é uma das melhores ferramentas disponíveis para validar e construir seu regex. Além de ter uma ótima ferramenta de teste de regex em tempo real, o Regexr também inclui uma folha de dicas e uma excelente documentação.
  • Regexstorm usa especificamente o motor .Net para impulsionar sua ferramenta. O site não possui todos os recursos que sites como o Regexr têm, mas irá testar com precisão sua expressão regular da mesma forma que o PowerShell faz. Mesmo que você use outras ferramentas para construir seu regex, você sempre deve executá-lo no regexstorm para garantir que o PowerShell o analise corretamente.

Não use PowerShell e Regex se não for necessário!

O PowerShell trabalha com objetos. O PowerShell é construído em torno de objetos estruturados. Objetos com propriedades são muito mais fáceis de gerenciar do que texto solto, onde o regex entra em cena.

Relacionado: Voltando ao Básico: Compreendendo Objetos no PowerShell

Um dos principais propósitos do PowerShell e também de linguagens estruturadas como JSON é tornar o regex e a análise de texto obsoletos. A linguagem humana é fantástica para o regex decifrar, mas geralmente, o regex é algo que você tenta evitar ao armazenar ou transferir dados.

Relacionado: Manipulando APIs REST com PowerShell e JSON

Algumas pessoas até ficam exasperadas ao usar regex em linguagens estruturadas.

Se você pode usar um método orientado a objetos ou uma linguagem estruturada como JSON, XML, etc., em vez de regex, faça isso! Mesmo que você possa fazer praticamente qualquer coisa com regex, não significa que você deve!

Continuando com o Regex

Com este artigo, agora você deveria ter uma compreensão básica de como o regex ajuda as máquinas a analisar e encontrar texto, mesmo ao procurar frases altamente específicas ou complicadas. Você também deve ter as ferramentas para testar, validar e aprender sobre regex no contexto do PowerShell.

Se você ainda não o fez, os tutoriais do RegexOne são um próximo passo fantástico. Teste seus conhecimentos em regex e aumente suas habilidades de manipulação de strings no PowerShell!

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