Windows PowerShellTeoria da cadeia de caracteres

Don Jones

Conteúdo

Um caso do mundo real
Correspondências complexas
Este é um mundo cheio de cadeia de caracteres

Quando falo em conferências como, por exemplo, Tech•Ed ou TechMentor, tenho o hábito de fazer proclamações – anúncios de regra geral que ajudam os usuários a se lembrar dos pontos principais relacionados a coisas como o Windows PowerShell. A minha última proclamação foi: "se está analisando uma cadeia de caracteres no Windows PowerShell, você está fazendo algo errado".

Isso provém da minha filosofia a respeito de o Windows PowerShell™ ser um shell orientado a objeto. Caso esteja fazendo coisas como despejar listas de serviços em um arquivo de texto e, em seguida, analisando esse arquivo de texto para ver quais são os serviços iniciados, você está trabalhando demais. Trata-se de uma abordagem válida em um sistema operacional baseado em texto como, por exemplo, UNIX, mas o Windows PowerShell (bem como o próprio Windows®) permite usar objetos de maneira muito mais eficiente.

Até mesmo os scripts devem produzir objetos, e não texto formatado, para que os vários comandos de formatação, filtragem, exportação e outros do shell possam ser usados para manipular a saída dos scripts. (Na minha coluna Windows PowerShell de julho de 2008, é possível ler mais sobre o conceito de objetos personalizados como saída de script.)

Durante uma TechMentor recente em Orlando, Flórida, um dos meus alunos me lembrou de que praticamente todas as regras, especialmente as gerais, têm exceções. "O que acha de localizar coisas em um arquivo de log do IIS? Você não tem que analisar o texto nesse caso?"

Bem, sim. E, felizmente, por ser amigável ao objeto como deve ser, o Windows PowerShell é muito eficiente ao analisar cadeias de caracteres de texto. E já que você mencionou, os arquivos de log do IIS, os arquivos de log do firewall e os demais logs baseados em texto são exemplos perfeitos.

Um caso do mundo real

Tive mesmo de analisar um conjunto de arquivos de log de firewall para a empresa para a qual trabalhava. Um funcionário foi pego acessando alguns sites inapropriados e, como parte da investigação contínua, o departamento de Recursos Humanos precisava de uma lista abrangente dos sites que ele andara visitando. Como era bem difícil obter algo de um arquivo de log referente a um único dia, o pessoal do RH queria retroceder algumas semanas – uma tarefa que eu não poderia fazer manualmente.

O servidor DHCP (Dynamic Host Configuration Protocol) da empresa indicou que o computador do funcionário estava usando o mesmo endereço IP (digamos, 192.168.17.54, tendo em vista este exemplo) há muitos meses. Isso, obviamente, não é algo incomum porque se tratava de um computador desktop que era raramente desligado. E como o log do firewall registrava os endereços IP de origem, eu sabia que o Windows PowerShell poderia ajudar bastante.

O segredo está no comando Select-String, muitas vezes deixado de lado. Além disso, você também precisará de um conhecimento funcional de expressões regulares (as quais abordei na edição de novembro de 2007 do Windows PowerShell).

O comando Select-String aceitará um caminho de arquivo repleto de arquivos de texto, uma expressão regular ou uma cadeia de caracteres para pesquisa. Em seguida, ele produzirá cada uma das linhas dos arquivos de log correspondentes à expressão regular ou à cadeia de caracteres simples. Para começar a minha tarefa, só queria obter todas as linhas que contivessem o endereço IP do computador desktop do funcionário. Todas as linhas do arquivo de log apresentavam um carimbo de data e hora, o que o pessoal do departamento de Recursos Humanos procurava.

Eis o comando:

select-string -path c:\logs\*.txt -pattern "192.168.17.54" 
-allmatches –simplematch

O parâmetro –simpleMatch especifica que o padrão fornecido é apenas uma cadeia de caracteres simples, e não uma expressão regular. A Figura 1 mostra parte da saída, que também poderia ser passada para um arquivo. É importante observar que a saída inclui tanto o nome do arquivo quanto o número da linha em que a correspondência foi encontrada, o que pode ser muito útil caso você queira retornar a algum ponto e procurar mais informações.

Cmdlet do mês: Start-Sleep

Eis um cmdlet que pode proporcionar exatamente aquilo de que você precisa no meio de um longo dia de trabalho – um cochilo. Start-Sleep oferece uma parada rápida – para os scripts do Windows PowerShell, portanto.

Não é incomum precisar de uma pausa rápida em um script. Por exemplo, digamos que você precise iniciar um serviço, aguardar alguns segundos para que ele esteja em funcionamento e, em seguida, realizar outras tarefas que dependam desse serviço. Start-Sleep é exatamente aquilo de que você precisa para esse comportamento. A execução de Start-Sleep 10, por exemplo, fará com que o shell pause durante 10 segundos. Caso precise de um controle mais preciso, você pode executar Start-Sleep -milli 100, por exemplo, para pausar durante 100 milissegundos. Start-Sleep suspende completamente o shell, inclusive scripts, pipelines e tudo o mais, durante o tempo especificado. Agora só se alguém escrevesse o cmdlet Start-Nap pelo qual sou louco.

fig01.gif

Figura 1 Saída de um comando Select-String (Clique na imagem para ampliá-la)

Correspondências complexas

Depois que forneci ao pessoal do RH exatamente o que queriam, eles acabaram percebendo que não era aquilo que procuravam. O meu relatório incluía visitas a inúmeros endereços IP como, por exemplo, 207.68.172.246 (o site do MSN®). A solicitação seguinte do investigador era cortar o meu relatório para incluir apenas as visitas a um determinado endereço IP, identificado por eles como sendo um dos sites em questão. Nesta coluna, não revelarei o endereço IP real que surgiu na investigação. Em seu lugar, usarei 207.68.172.246 para este exemplo (muito embora o site do MSN não seja considerado inapropriado normalmente).

Essa solicitação poderia ser um pouco mais difícil. No arquivo de log no qual estava trabalhando, os endereços IP de origem e destino estavam bem próximos um do outro e separados por uma vírgula. Logo, bastaria alterar a minha cadeia de caracteres de pesquisa para "192.168.17.54,207.68.172.246" e repetir a pesquisa.

No entanto, em um arquivo de log mais complexo, é possível que houvesse dados variáveis armazenados entre os dois endereços IP e, assim, uma correspondência da cadeia de caracteres simples não funcionaria. Nesse cenário, você teria de recorrer a uma expressão regular, que também funcionará bem em formatos de log mais simples, e o que demonstrarei aqui.

Em uma expressão regular, o caractere de ponto final é um curinga para qualquer caractere. Além disso, é possível usar a subexpressão (.)* para procurar um número qualquer de caracteres entre os meus dois endereços IP. No entanto, é necessário usar uma barra invertida para escapar os pontos finais literais exibidos nos próprios endereços IP.

O comando resultante é:

select-string -path c:\logs\*.txt -pattern 
"192\.168\.17\.54(.)*207\.68\.172\.246" –allmatches

Removi o parâmetro –simpleMatch porque estou usando uma expressão regular desta vez. A saída resultante só mostrou as visitas feitas do computador do funcionário para o site específico identificado como inapropriado. A saída também incluiu as informações do carimbo de data e hora que o investigador queria. A Figura 2 mostra uma parte da saída que você deve obter ao executar um comando como este.

fig02.gif

Figura 2 Resultados de pesquisa detalhados que mostram apenas as visitas a um determinado site (Clique na imagem para ampliá-la)

Mas é possível dar um passo além e passar a saída para Format-Table e contar com a possibilidade de exibir colunas calculadas. Posso incluir na tabela o nome do arquivo de log e o número da linha na qual a correspondência foi encontrada, e ainda exibirei a própria linha correspondente. No entanto, o shell pode substituir a correspondência da expressão regular por uma cadeia de caracteres vazia para que apenas o restante da linha – o carimbo de data e hora do meu exemplo – seja exibido. Embora seja avançado, esse truque é mais uma demonstração de como o Windows PowerShell pode manipular os dados da cadeia de caracteres e produzir uma saída altamente personalizável, tudo em uma única linha de comando:

select-string -path c:\logs\*.txt -pattern 
"192\.168\.17\.54(.)*207\.68\.172\.246" -allmatches | 
ft  filename,linenumber,@{"Label"="Time";
"Expression"={$_.line.replace
($_.matches[0],"")}} –auto

A Figura 3 mostra como devem ser os meus resultados finais.

fig03.gif

Figura 3 Saída formata de um comando Select-String (Clique na imagem para ampliá-la)

Este é um mundo cheio de cadeia de caracteres

Sou rápido em proclamar a natureza orientada a objeto do Windows PowerShell como um dos seus pontos mais positivos. Porém, há momentos em que os objetos deixam de ser uma opção.

O Windows PowerShell pode viver em um mundo orientado a objeto. Felizmente, a equipe do Windows PowerShell reconheceu que o mundo costuma conter dados externos em cadeias de caracteres formatadas e acabou incluindo o comando Select-String. Contando com Select-String e alguma familiaridade com expressões regulares, você pode usar o Windows PowerShell para escrever linhas únicas que analisarão as cadeias de caracteres mais complicadas.

Don Jones é co-autor de Windows PowerShell: TFM e autor de dezenas de outros livros sobre TI. Entre em contato com ele por meio do seu blog em www.concentratedtech.com.