No PowerShell, comparar objetos é uma tarefa comum, seja para verificar diferenças entre arquivos, processos, serviços ou qualquer coleção de dados. O cmdlet Compare-Object é a ferramenta central para essa finalidade, permitindo identificar itens que estão presentes em um conjunto, mas ausentes em outro, e vice-versa. Dominar essa ferramenta é essencial para automatizar tarefas de administração de sistemas, auditoria e validação de configurações.

Nesta aula, vamos explorar o Compare-Object em profundidade, desde a sintaxe básica até casos de uso avançados, com exemplos práticos que você pode testar imediatamente. Vamos também discutir boas práticas para evitar armadilhas comuns e aproveitar ao máximo esse poderoso cmdlet.

Compare-Object

O cmdlet Compare-Object compara dois conjuntos de objetos (referência e diferença) e retorna as diferenças encontradas. Ele é semelhante ao comando diff do Linux, mas funciona com objetos do PowerShell, não apenas com texto. A sintaxe básica é:

Compare-Object -ReferenceObject <coleção1> -DifferenceObject <coleção2>

O parâmetro -ReferenceObject define o conjunto de referência (o "original"), e -DifferenceObject define o conjunto a ser comparado (o "novo"). O cmdlet retorna objetos que estão presentes em apenas um dos conjuntos, indicando a direção da diferença com um indicador (<= para referência, => para diferença, e == para itens iguais, se você usar o parâmetro -IncludeEqual).

Por padrão, o Compare-Object compara objetos por valor, não por referência. Para objetos complexos, você pode especificar quais propriedades comparar usando o parâmetro -Property. Se não especificar, ele compara todas as propriedades. Vamos ver um exemplo simples:

$a = 1,2,3,4,5
$b = 1,2,3,6,7
Compare-Object $a $b

Isso retornará:

InputObject SideIndicator
----------- -------------
          4 <=
          5 <=
          6 =>
          7 =>

Onde <= indica itens presentes apenas em $a, e => apenas em $b. Essa saída clara facilita a interpretação das diferenças.

Diferenças

As diferenças podem ser de vários tipos: valores diferentes, propriedades ausentes, ou objetos completamente distintos. O Compare-Object lida com todos esses casos. Para entender melhor, vamos considerar a comparação de arquivos. Suponha que você tenha dois arquivos de configuração e queira saber quais linhas mudaram. Você pode usar Get-Content para ler as linhas e compará-las:

$old = Get-Content C:\config\app.config
$new = Get-Content C:\config\app_new.config
Compare-Object $old $new

Isso mostrará as linhas que foram adicionadas ou removidas. No entanto, essa comparação é sensível à ordem; se as linhas forem reordenadas, todas aparecerão como diferenças. Para comparar por conteúdo sem considerar a ordem, você pode usar Sort-Object antes:

Compare-Object ($old | Sort-Object) ($new | Sort-Object)

Outra diferença importante é a comparação de objetos complexos, como processos. Você pode comparar a lista de processos em execução em dois momentos diferentes para descobrir quais foram iniciados ou encerrados. Por exemplo:

$before = Get-Process
Start-Process notepad
$after = Get-Process
Compare-Object $before $after -Property ProcessName

Isso retornará o processo notepad com o indicador =>, indicando que ele está presente apenas em $after. Note que usamos -Property para comparar apenas o nome do processo, pois outros atributos como ID ou memória podem variar.

Casos de uso

O Compare-Object é extremamente versátil e pode ser aplicado em diversos cenários:

  • Auditoria de alterações em arquivos: comparar versões de arquivos de configuração ou logs para identificar mudanças.
  • Monitoramento de processos e serviços: verificar quais processos ou serviços foram iniciados/parados entre dois momentos.
  • Validação de dados: comparar listas de usuários, computadores ou outros objetos para encontrar discrepâncias.
  • Testes automatizados: verificar se a saída de um comando corresponde ao esperado.
  • Sincronização de diretórios: comparar o conteúdo de duas pastas para identificar arquivos a copiar ou excluir.

Além disso, você pode combinar Compare-Object com outros cmdlets para criar scripts poderosos. Por exemplo, para listar arquivos que existem em uma pasta, mas não em outra:

$folder1 = Get-ChildItem C:\Folder1 -Recurse | Select-Object FullName
$folder2 = Get-ChildItem C:\Folder2 -Recurse | Select-Object FullName
Compare-Object $folder1 $folder2 | Where-Object { $_.SideIndicator -eq '<=' } | ForEach-Object { $_.InputObject }

Isso retorna os caminhos completos dos arquivos presentes apenas na primeira pasta.

Exemplos

Vamos explorar alguns exemplos práticos para consolidar o aprendizado.

Exemplo 1: Comparando listas de números

$lista1 = 1..10
$lista2 = 5..15
Compare-Object $lista1 $lista2

Saída:

InputObject SideIndicator
----------- -------------
          1 <=
          2 <=
          3 <=
          4 <=
         11 =>
         12 =>
         13 =>
         14 =>
         15 =>

Exemplo 2: Comparando arquivos com hash

Para comparar o conteúdo de dois arquivos binários, você pode calcular o hash e compará-lo:

$hash1 = Get-FileHash C:\File1.bin -Algorithm SHA256
$hash2 = Get-FileHash C:\File2.bin -Algorithm SHA256
Compare-Object $hash1 $hash2

Se os hashes forem iguais, não haverá saída; caso contrário, você verá a diferença.

Exemplo 3: Comparando propriedades específicas

Suponha que você tenha duas listas de usuários e queira comparar apenas os nomes de usuário:

$usuarios1 = Get-ADUser -Filter * -Properties SamAccountName | Select-Object SamAccountName
$usuarios2 = Get-ADUser -Filter * -Properties SamAccountName -SearchBase "OU=Temp,DC=dominio,DC=com" | Select-Object SamAccountName
Compare-Object $usuarios1 $usuarios2 -Property SamAccountName

Isso mostrará quais usuários estão em uma lista mas não na outra.

Exemplo 4: Usando -IncludeEqual e -ExcludeDifferent

Para ver apenas os itens iguais, use -IncludeEqual -ExcludeDifferent:

Compare-Object (1,2,3) (1,2,4) -IncludeEqual -ExcludeDifferent

Saída:

InputObject SideIndicator
----------- -------------
          1 ==
          2 ==

Boas práticas e observações finais

Ao usar Compare-Object, lembre-se de:

  • Use -Property para comparar apenas as propriedades relevantes, evitando falsos positivos devido a campos irrelevantes.
  • Para grandes conjuntos de dados, considere usar Sort-Object antes para melhorar o desempenho, embora o cmdlet já ordene internamente.
  • Entenda a diferença entre <=, => e == para interpretar corretamente a saída.
  • Quando comparar arquivos, use Get-FileHash para verificar igualdade de conteúdo, em vez de comparar linhas, se a ordem não importar.

O Compare-Object é uma ferramenta indispensável no arsenal de qualquer profissional que trabalha com PowerShell. Com a prática, você descobrirá muitos outros usos criativos.

Exercícios

  1. Escreva um comando para comparar as duas listas a seguir e exibir apenas os números que estão em $lista1 mas não em $lista2.

    $lista1 = 1,2,3,4,5
    $lista2 = 3,4,5,6,7

    ✓ Resposta:
    Compare-Object $lista1 $lista2 | Where-Object { $_.SideIndicator -eq '<=' } | ForEach-Object { $_.InputObject }
  2. Como você compararia duas listas de processos (obtidas com Get-Process) para descobrir quais processos foram iniciados entre a coleta de $antes e $depois? Use apenas o nome do processo como critério.

    ✓ Resposta:
    Compare-Object $antes $depois -Property ProcessName | Where-Object { $_.SideIndicator -eq '=>' }
  3. Explique o que o parâmetro -IncludeEqual faz e como ele altera a saída do Compare-Object.

    ✓ Resposta: O parâmetro -IncludeEqual faz com que o cmdlet inclua na saída os itens que são iguais em ambos os conjuntos, com o indicador ==. Sem ele, apenas diferenças são exibidas.
  4. Qual é a diferença entre comparar arquivos usando Get-Content e comparar usando Get-FileHash? Quando você usaria cada um?

    ✓ Resposta: Get-Content compara o conteúdo linha por linha, útil para arquivos de texto onde a ordem importa. Get-FileHash compara a integridade do arquivo inteiro (hash), útil para verificar se dois arquivos são idênticos, independentemente da ordem ou formatação. Use Get-Content para diferenças detalhadas em texto, e Get-FileHash para verificação rápida de igualdade.
  5. Escreva um script que compare o conteúdo de duas pastas (incluindo subpastas) e liste os arquivos que estão apenas na primeira pasta. Considere comparar pelo caminho completo.

    ✓ Resposta:
    $pasta1 = Get-ChildItem C:\Pasta1 -Recurse | Select-Object -ExpandProperty FullName
    $pasta2 = Get-ChildItem C:\Pasta2 -Recurse | Select-Object -ExpandProperty FullName
    Compare-Object $pasta1 $pasta2 | Where-Object { $_.SideIndicator -eq '<=' } | ForEach-Object { $_.InputObject }

Referências