Windows PowerShellEscrevendo expressões regulares

Don Jones

192.168.4.5. \\Server57\Share. johnd@contoso.com. Você não tem dúvidas ao reconhecer esses três itens como sendo um endereço IP, um caminho UNC (Universal Naming Convention) e um endereço de email. O seu cérebro reconhece os formatos. Quatro agrupamentos de dígitos, barras invertidas, o símbolo @ e as demais pistas indicam quais tipos de dados essas cadeias de

caracteres representam. Raciocinando um pouquinho, você reconhece rapidamente que apenas 192.168 não é um endereço IP válido, que 7\\Server2\\Share não é um UNC válido e que joe@contoso também não é um endereço de email válido.

Infelizmente, os computadores precisam trabalhar um pouco mais para poder "compreender" formatos complicados como esse. É onde entram as expressões regulares. Uma expressão regular é uma cadeia de caracteres, escrita com uma linguagem especial de expressão regular, que ajuda um computador a identificar as cadeias de um determinado formato – como, por exemplo, um endereço IP, um UNC ou um endereço de email. Uma expressão regular bem escrita tem a possibilidade de permitir que um script Windows PowerShellTM aceite como válidos ou rejeite como inválidos dados incompatíveis com o formato especificado por você.

Estabelecendo uma correspondência simples

O operador –match do Windows PowerShell compara uma cadeia de caracteres com uma expressão regular, ou regex, e retorna Verdadeiro ou Falso, dependendo da correspondência da cadeia em relação a regex. Um regex muito simples não precisa sequer conter nenhuma sintaxe especial – bastarão caracteres literais. Por exemplo:

"Microsoft" –match "soft"
"Software" –match "soft"
"Computers" –match "soft"

Quando executadas no Windows PowerShell, as duas primeiras expressões retornam Verdadeiro e a terceira, Falso. Em cada uma delas, uma cadeia é seguida do operador –match, seguido, por sua vez, por um regex. Por padrão, um regex flutuará em uma cadeia de caracteres até encontrar uma correspondência. Os caracteres "soft" podem ser encontrados tanto em Software quanto em Microsoft, mas em posições diferentes. Também observe que, por padrão, um regex não diferencia maiúsculas de minúsculas – "soft" é encontrado em "Software", apesar da letra S em maiúscula.

Mas caso isso seja necessário, um operador diferente, –cmatch, oferece uma comparação de regex diferenciando maiúsculas de minúsculas, como:

"Software" –cmatch "soft"

Essa expressão retorna Falsa, já que a cadeia de caracteres "soft" não corresponde a "Software" em uma comparação que diferencia maiúsculas de minúsculas. Observe que o operador –imatch também está disponível como uma opção explícita que não diferencia maiúsculas de minúsculas, embora esse seja o comportamento padrão de –match.

Curingas e repetidores

Um regex pode conter alguns caracteres curinga. Um ponto final, por exemplo, corresponde a uma instância de qualquer caractere. Um ponto de interrogação corresponde a zero ou a uma instância de qualquer caractere. Estes são alguns exemplos de ilustração:

"Don" –match "D.n" (True)
"Dn" –match "D.n" (False)
"Don" –match "D?n" (True)
"Dn" –match "D?n" (True)

Na primeira expressão, como o ponto final significa exatamente um caractere, a correspondência é Verdadeira. Já na segunda, como o ponto final não encontra o caractere necessário à inclusão, a correspondência é Falsa. O ponto de interrogação, como mostrado na terceira e na quarta expressões, pode corresponder a um único caractere desconhecido ou a nenhum caractere. Por fim, no quarto exemplo, a correspondência é Verdadeira porque tanto "D" quanto "n" são encontrados sem que haja um caractere entre eles. Por isso, como o ponto de interrogação pode ser considerado um espaço fixo para um caractere opcional, a correspondência continua sendo Verdadeira, mesmo que nenhum caractere seja exibido nessa posição.

Um regex também reconhece os símbolos * e + como repetidores. Eles precisam acompanhar um ou mais caracteres. O * corresponde a zero ou mais e + corresponde a um ou mais dos caracteres especificados. Estes são alguns exemplos:

"DoDon" –match "Do*n" (True)
"Dn" -match "Do*n" (True)
"DoDon" -match "Do+n" (True)
"Dn" -match "Do+n" (False)

Observe que * e + correspondem a "Do", e não apenas a "o". Isso porque esses repetidores foram criados para corresponder a uma série de caracteres, e não apenas a um caractere.

E caso seja necessária uma correspondência ao ponto final, *, ? ou aos próprios símbolos +? Basta precedê-los com uma barra invertida, que é um caractere de escape regex:

"D.n" -match "D\.n" (True)

Observe que isso é diferente do caractere de escape do Windows PowerShell (o apóstrofo invertido), embora ele siga a sintaxe regex padrão do setor.

Classes de caractere

Uma classe de caractere é uma forma mais abrangente de curinga, que representa um grupo inteiro de caracteres. O Windows PowerShell reconhece algumas classes de caractere. Por exemplo:

  • \w corresponde a qualquer caractere da palavra, o que significa letras e números.
  • \s corresponde a qualquer caractere de espaço em branco como, por exemplo, tabulações, espaços etc.
  • \d corresponde a qualquer caractere de dígito.

Também há classes de caractere negativas: \W corresponde a qualquer caractere que não seja uma palavra, \S corresponde a caracteres que não sejam espaços em branco e \D corresponde a tudo que não seja um dígito. Essas classes podem ser seguidas por * ou + para indicar se várias correspondências são aceitáveis. Estes são alguns exemplos:

"Shell" -match "\w" (True)
"Shell" -match "\w*" (True)

Cmdlet do mês

O cmdlet Write-Debug é muito prático para escrever objetos (como, por exemplo, cadeias de texto) no pipeline Debug. Mas testar esse cmdlet no shell pode ser um tanto desapontador, porque parece que o cmdlet não está fazendo nada.

O truque é que o pipeline Debug é desativado por padrão – a variável $DebugPreference é definida como "SilentlyContinue". No entanto, defina-a como "Continue", e tudo o que você enviar usando Write-Debug será exibido no console em texto amarelo. Trata-se de uma forma perfeita para adicionar código de rastreamento aos scripts, o que lhe permite acompanhar a execução de um script complexo. A cor amarela ajuda você a diferenciar o rastreamento da saída normal do script; além disso, é possível desativar as mensagens de depuração a qualquer momento sem que seja preciso remover todas as instruções Write-Debug. Basta definir $DebugPreference = "SilentlyContinue" novamente, e o texto da depuração será suprimido.

Embora ambas as expressões retornem Verdadeiro, elas correspondem a coisas bem diferentes. Felizmente, existe uma forma de ver o que o operador –match está pensando na realidade: sempre que uma correspondência é estabelecida, um caractere especial chamado $matches é preenchido com os resultados da correspondência – ou seja, qualquer caractere da cadeia para o qual o operador encontrou uma correspondência no regex. A variável $matches retém os resultados até que seja estabelecida outra correspondência positiva usando o operador –match. A Figura 1 mostra a diferença entre as duas expressões que acabei de apresentar a você. Como você pode ver, \w encontrou uma correspondência para "S" em "Shell" e \w* de repetição estabeleceu uma correspondência com a palavra inteira.

Figura 1 Que diferença um * pode fazer

Figura 1** Que diferença um * pode fazer **(Clique na imagem para aumentar a exibição)

Grupos, intervalos e tamanhos de caracteres

Um regex também pode conter grupos ou intervalos de caracteres, entre colchetes. Por exemplo, [aeiou] significa que qualquer um dos caracteres incluídos – a, e, i, o ou u – é uma correspondência aceitável. [a-zA-Z] indica que qualquer letra do intervalo a-z ou A-Z é aceitável (mesmo que você estivesse usando o operador –match que diferencia maiúsculas de minúsculas, apenas a-z ou A-Z seria o suficiente). Por exemplo:

"Jeff" -match "J[aeiou]ff" (True)
"Jeeeeeeeeeeff" -match "J[aeiou]ff" (False)

Também é possível especificar um número mínimo e máximo de caracteres usando chaves. {3} indica que você deseja exatamente três instâncias do caractere especificado, {3,} significa que você deseja pelo menos três ou mais e {3,4} indica que você deseja pelo menos três, mas não mais que quatro. Esta é a forma ideal de criar um regex para endereços IP:

"192.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

Esse regex quer quatro grupos de dígitos com entre um e três dígitos cada, todos separados por um ponto final literal. Mas considere este exemplo:

"300.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

Isso mostra as limitações de um regex. Embora a formatação da cadeia de caracteres seja semelhante a um endereço IP, é claro que não se trata de um endereço IP válido. Um regex é incapaz de determinar se os dados são efetivamente válidos; ele só consegue determinar se eles parecem estar corretos.

Parar o float

Solucionar problemas de um regex pode ser um pouco confuso. Por exemplo, aqui está um regex que testa um caminho UNC no formato \\Server2\Share:

"\\Server2\Share" -match "\\\\\w+\\\w+" (True)

Aqui, a leitura até do próprio regex está difícil porque todas as barras invertidas literais que eu desejo testar precisam ter um escape com uma segunda barra invertida. Embora pareça funcionar bem, isso não acontece na realidade:

"57\\Server2\Share" -match "\\\\\w+\\\w+" (True)

Este segundo exemplo é claramente (pelo menos para mim e para você) um caminho UNC inválido, muito embora o regex tenha aceitado isso sem problemas. Por quê? Lembre-se de que, por padrão, um regex flutua. Este regex basicamente procura duas barras invertidas, uma ou mais letras e números, outra barra invertida e mais letras e números. Esse padrão existe na cadeia de caracteres – com os dígitos a mais no início, o que o torna um UNC inválido. O truque é informar ao regex para começar a correspondência no início da cadeia, sem flutuação. Eu posso fazer algo assim:

"57\\Server2\Share" -match "^\\\\\w+\\\w+" (False)

O caractere ^ indica o local em que a cadeia de caracteres começa. Com esse acréscimo, há uma falha no caminho UNC inválido porque o regex procura os dois primeiros caracteres como barras invertidas e, nesse caso, eles não são.

Da mesma forma, o símbolo $ pode ser usado para indicar o final de uma cadeia. Isso não seria muito útil no caso de um caminho UNC porque este pode conter segmentos de caminho adicionais como, por exemplo, \\Server2\Share\Folder\File. No entanto, tenho certeza de que há muitos casos em que você gostaria de especificar o final de uma cadeia de caracteres.

Ajuda com expressões regulares

No Windows PowerShell, o tópico da ajuda about_regular_expressions oferece assistência básica de sintaxe relacionada à linguagem regex, mas alguns recursos online podem fornecer ainda mais informações. Por exemplo, um dos meus sites favoritos, www.RegExLib.com, oferece uma biblioteca gratuita de expressões regulares que foram escritas com várias finalidades como contribuições dadas pelas pessoas que o visitam. É possível pesquisar usando palavras-chave como, por exemplo, "e-mail" ou "UNC", para localizar rapidamente um regex que atenda à sua necessidade – ou pelo menos ofereça um bom começo. Caso pretenda criar um bom regex, você pode contribuir com ele para a biblioteca de forma que as outras pessoas também possam usá-lo.

Eu também gosto do RegexBuddy (www.RegexBuddy.com). Trata-se de uma ferramenta de baixo custo que oferece um editor gráfico de regex. RegexBuddy facilita a montagem de um regex complexo, e essa ferramenta também simplifica o teste de um regex para garantir que ele aceite corretamente cadeias de caracteres válidas e rejeite as inválidas. Outros vários desenvolvedores de software também criaram editores de regex gratuitos, shareware e comerciais, além de testadores, que os usuários certamente considerarão úteis.

Usando expressões regulares

Você deve estar se perguntando por que usaria um regex na vida real. Imagine que você esteja lendo informações de um arquivo CSV e as usando na criação de novos usuários no Active Directory®. Se o arquivo CSV for gerado por outra pessoa, você precisará validar se os dados contidos nele estão corretos. Um regex é perfeito para a tarefa. Um regex simples como, por exemplo, \w+, pode confirmar se os nomes e os sobrenomes não contêm nenhum caractere ou símbolo especial, enquanto algo um pouco mais rebuscado pode confirmar se os endereços de email estão de acordo com o seu padrão corporativo. Por exemplo, você poderia usar:

"^[a-z]+\.[a-z]+@contoso.com$"

Esse regex exige que um endereço de email esteja na forma don.jones@contoso.com, em que os nomes e os sobrenomes só podem conter letras e devem estar separados por um ponto final. A propósito, os endereços de email são as cadeias de caracteres mais complexas para se escrever um regex. Se puder restringir o escopo a um padrão corporativo específico, você terá mais facilidade nele.

Não se esqueça das âncoras inicial e final (^ e $), que garantem que não haja nada após contoso.com e também que nada preceda os caracteres que formam o nome do usuário.

Na verdade, usar esse regex no Windows PowerShell é muito simples. Pressupondo que a variável $email contenha o endereço de email lido no arquivo CSV, isto deverá verificar se ele é válido ou não:

$regex = "^[a-z]+\.[a-z]+@contoso.com$"
If ($email –notmatch $regex) {
  Write-Error "Invalid e-mail address $email" 
}

Nesse exemplo, você aprendeu um novo operador. -notmatch retorna Verdadeiro caso a cadeia de caracteres não corresponda ao regex fornecido. (Também há um –cnotmatch para comparações diferenciando maiúsculas de minúsculas.)

Há muito mais sobre expressões regulares do que eu abordei aqui – classes de caractere adicionais, mais operações avançadas e até mesmo um operador ou dois. E ainda há o tipo de objeto [regex] para o qual o Windows PowerShell dá suporte. No entanto, o que eu abordei nessa rápida visão geral da sintaxe regex deve ser o suficiente para você começar. Sinta-se à vontade para me visitar em www.ScriptingAnswers.com caso você precise de ajuda em meio a um regex especialmente confuso.

Don Jones é editor colaborador da TechNet Magazine e co-autor de Windows PowerShell: TFM (SAPIEN Press). Ele é professor de Windows PowerShell (www.ScriptingTraining.com) e pode ser contatado pelo site ScriptingAnswers.com.

© 2008 Microsoft Corporation e CMP Media, LLC. Todos os direitos reservados. A reprodução parcial ou completa sem autorização é proibida..