Nesta aula, vamos explorar uma das operações mais comuns em scripts: a leitura de arquivos. Dominar a leitura de arquivos é essencial para processar logs, configurações, dados de entrada e muito mais. O PowerShell oferece o cmdlet Get-Content, que é a ferramenta principal para essa tarefa. Vamos entender suas variações, parâmetros e boas práticas para evitar armadilhas comuns, como problemas de encoding e desempenho.

Além de ler o conteúdo, você aprenderá a controlar como os dados são apresentados: como um array de linhas, como uma única string ou processando linha por linha. Isso influencia diretamente como você manipula os dados no seu script. Vamos também discutir a importância de especificar a codificação correta para evitar caracteres corrompidos.

Get-Content

O cmdlet Get-Content é o equivalente ao cat do Linux ou ao type do CMD. Ele lê um arquivo e retorna seu conteúdo. Por padrão, ele retorna um array de strings, onde cada elemento é uma linha do arquivo. Isso é extremamente útil para processar arquivos linha por linha.

Vamos começar com um exemplo simples. Suponha que temos um arquivo chamado exemplo.txt com o seguinte conteúdo:

# exemplo.txt
Linha 1
Linha 2
Linha 3

Para ler o arquivo, usamos:

Get-Content -Path C:\temp\exemplo.txt

Isso exibirá cada linha no console. O resultado é um array de strings, o que significa que podemos armazená-lo em uma variável e acessar linhas específicas por índice (começando em 0).

$linhas = Get-Content -Path C:\temp\exemplo.txt
$linhas[0]  # Retorna "Linha 1"
$linhas.Count  # Retorna 3

O parâmetro -Path aceita curingas, permitindo ler vários arquivos de uma vez. Por exemplo, para ler todos os arquivos .log de um diretório:

Get-Content -Path C:\logs\*.log

Nesse caso, o conteúdo de todos os arquivos é concatenado em um único array, sem separadores entre arquivos. Se precisar saber de qual arquivo veio cada linha, é melhor usar outros cmdlets como Get-ChildItem em conjunto.

-Raw

Às vezes, você quer ler o arquivo como uma única string, sem divisão em linhas. Para isso, use o parâmetro -Raw. Isso é útil quando você precisa tratar o conteúdo como um todo, por exemplo, para fazer uma substituição global ou para analisar XML, JSON ou outros formatos que não são linha a linha.

$conteudoCompleto = Get-Content -Path C:\temp\exemplo.txt -Raw

Agora $conteudoCompleto é uma string única que contém tudo, incluindo as quebras de linha. Você pode verificar:

$conteudoCompleto.GetType().Name  # String
$conteudoCompleto.Length  # Número total de caracteres

Uma aplicação comum é substituir texto em todo o arquivo de uma vez:

$conteudo = Get-Content -Path arquivo.txt -Raw
$conteudo = $conteudo -replace 'foo', 'bar'
Set-Content -Path arquivo.txt -Value $conteudo

Sem -Raw, o -replace seria aplicado a cada linha separadamente, o que pode ser ineficiente e causar problemas se a substituição envolver quebras de linha.

Lendo linha a linha

Embora Get-Content retorne um array de linhas, carregar um arquivo inteiro na memória pode ser problemático para arquivos muito grandes. Para processar arquivos grandes de forma eficiente, você pode usar o parâmetro -ReadCount ou um loop foreach com o pipeline. O PowerShell permite que você processe cada linha à medida que é lida, sem armazenar tudo na memória.

Uma abordagem comum é usar o pipeline:

Get-Content -Path C:\logs\grande.log | ForEach-Object {
    # $_ contém a linha atual
    if ($_ -match 'erro') {
        Write-Host "Encontrado: $_"
    }
}

Nesse exemplo, cada linha é processada individualmente e descartada em seguida. Isso é eficiente para arquivos com milhões de linhas.

Outra opção é usar -ReadCount para ler um número específico de linhas por vez. O padrão é 1, mas você pode definir um valor maior para reduzir o número de chamadas ao disco. Por exemplo, para ler 1000 linhas por vez:

Get-Content -Path arquivo.txt -ReadCount 1000 | ForEach-Object {
    # $_ é um array com até 1000 linhas
    foreach ($linha in $_) {
        # processa cada linha
    }
}

Essa técnica é útil quando você precisa de um lote de linhas para processamento em conjunto, como para inserir em um banco de dados.

Também é importante conhecer a diferença entre Get-Content e o método [System.IO.File]::ReadLines() do .NET. O método ReadLines() também retorna um enumerador que lê linha por linha, mas é mais rápido em alguns cenários. Você pode usá-lo assim:

[System.IO.File]::ReadLines("C:\temp\arquivo.txt") | ForEach-Object {
    # processa a linha
}

Para a maioria dos scripts, Get-Content é suficiente, mas para arquivos extremamente grandes, considere usar ReadLines().

Encoding

Encoding (codificação) é a forma como os caracteres são representados em bytes. O PowerShell usa por padrão a codificação do sistema (geralmente UTF-8 sem BOM no PowerShell 7, e ANSI no Windows PowerShell 5.1). Ao ler arquivos com codificações diferentes, você pode obter caracteres corrompidos. Portanto, é essencial especificar a codificação correta ao ler e escrever arquivos.

O parâmetro -Encoding do Get-Content permite definir a codificação esperada. Os valores comuns incluem:

  • utf8 - UTF-8 (com ou sem BOM, dependendo da versão)
  • ascii - ASCII
  • unicode - UTF-16 LE
  • bigendianunicode - UTF-16 BE
  • utf8BOM - UTF-8 com BOM (disponível no PowerShell 7+)
  • Default - codificação ANSI do sistema

Exemplo:

Get-Content -Path arquivo.txt -Encoding utf8

Se você não souber a codificação, pode usar o cmdlet Format-Hex para inspecionar os bytes e identificar. Por exemplo:

Format-Hex -Path arquivo.txt | Select-Object -First 5

Isso mostra os primeiros bytes. Se começar com EF BB BF, é UTF-8 com BOM; se começar com FF FE, é UTF-16 LE; e assim por diante.

No PowerShell 7, o padrão é UTF-8 sem BOM, o que é uma mudança em relação ao Windows PowerShell 5.1 que usava ANSI. Isso pode causar problemas ao ler arquivos criados com codificação ANSI. Se você estiver trabalhando com arquivos legados, especifique -Encoding Default ou -Encoding Ansi (equivalente).

Para escrever arquivos com uma codificação específica, use Set-Content com o parâmetro -Encoding.

Uma boa prática é sempre especificar a codificação ao ler e escrever, especialmente em scripts que serão executados em diferentes ambientes. Isso evita surpresas com caracteres especiais, como acentos.

Boas práticas e observações finais

Ao trabalhar com leitura de arquivos, considere as seguintes boas práticas:

  • Sempre especifique a codificação ao ler e escrever arquivos, para garantir consistência.
  • Para arquivos grandes, evite carregar todo o conteúdo na memória; use pipeline ou -ReadCount.
  • Use -Raw quando precisar do conteúdo como uma única string, especialmente para substituições globais.
  • Verifique se o arquivo existe antes de tentar lê-lo, usando Test-Path, para evitar erros.
  • Considere usar Get-Content com -Tail para ler as últimas linhas de um arquivo, útil para logs.

Por exemplo, para ver as últimas 10 linhas de um log:

Get-Content -Path C:\logs\app.log -Tail 10

E para monitorar um log em tempo real, use -Wait (disponível no Windows PowerShell e PowerShell 7):

Get-Content -Path C:\logs\app.log -Tail 5 -Wait

Isso é útil para acompanhar logs enquanto o sistema grava.

Referências

Exercícios

  1. Crie um script que leia um arquivo chamado dados.txt e exiba o número total de linhas.

    ✓ Resposta:
    $linhas = Get-Content -Path dados.txt
    Write-Host "Total de linhas: $($linhas.Count)"
  2. Leia um arquivo usando -Raw e exiba o comprimento da string resultante.

    ✓ Resposta:
    $conteudo = Get-Content -Path dados.txt -Raw
    Write-Host "Comprimento: $($conteudo.Length)"
  3. Escreva um script que processe um arquivo linha a linha e imprima apenas as linhas que contêm a palavra "erro" (case-insensitive).

    ✓ Resposta:
    Get-Content -Path log.txt | ForEach-Object {
        if ($_ -match 'erro') {
            Write-Host $_
        }
    }
  4. Leia um arquivo com codificação específica (por exemplo, UTF-8) e mostre as três primeiras linhas.

    ✓ Resposta:
    Get-Content -Path dados.txt -Encoding utf8 | Select-Object -First 3
  5. Use -ReadCount para ler um arquivo em blocos de 5 linhas e exibir o número de blocos lidos.

    ✓ Resposta:
    $blocos = 0
    Get-Content -Path dados.txt -ReadCount 5 | ForEach-Object {
        $blocos++
    }
    Write-Host "Total de blocos: $blocos"