Nesta aula, vamos explorar como o PowerShell pode ser usado para automatizar tarefas recorrentes de gerenciamento de arquivos. Seja para organizar downloads, renomear arquivos em massa, limpar pastas temporárias ou gerar relatórios de uso, o PowerShell oferece cmdlets poderosos e uma sintaxe flexível que permitem criar scripts robustos e reutilizáveis.

Dominar a automação de arquivos é essencial para qualquer profissional de TI que deseja economizar tempo e reduzir erros manuais. Vamos começar com conceitos básicos e evoluir para exemplos práticos que você pode adaptar ao seu dia a dia.

Organização em lote

Organizar arquivos em pastas específicas é uma tarefa comum, mas tediosa quando feita manualmente. Com o PowerShell, podemos criar scripts que classificam arquivos por tipo, data ou outros critérios em questão de segundos. O cmdlet Move-Item é o principal aliado para mover arquivos, e podemos combiná-lo com Get-ChildItem para selecionar os arquivos que desejamos.

Por exemplo, suponha que você tenha uma pasta de downloads com arquivos de diferentes extensões e queira movê-los para subpastas como "Documentos", "Imagens" e "Vídeos". O script abaixo faz exatamente isso, criando as pastas se necessário e movendo os arquivos com base na extensão.

$pastaOrigem = "C:\Downloads"
$pastaDestino = "C:\Organizado"

# Definir o mapeamento de extensões para pastas
$mapa = @{
    ".docx" = "Documentos"
    ".pdf"  = "Documentos"
    ".jpg"  = "Imagens"
    ".png"  = "Imagens"
    ".mp4"  = "Vídeos"
    ".avi"  = "Vídeos"
}

# Criar a pasta de destino se não existir
if (-not (Test-Path $pastaDestino)) {
    New-Item -ItemType Directory -Path $pastaDestino | Out-Null
}

# Obter todos os arquivos da pasta de origem
Get-ChildItem -Path $pastaOrigem -File | ForEach-Object {
    $extensao = $_.Extension.ToLower()
    if ($mapa.ContainsKey($extensao)) {
        $pastaAlvo = Join-Path $pastaDestino $mapa[$extensao]
        # Criar a subpasta se não existir
        if (-not (Test-Path $pastaAlvo)) {
            New-Item -ItemType Directory -Path $pastaAlvo | Out-Null
        }
        Move-Item -Path $_.FullName -Destination $pastaAlvo -Force
        Write-Host "Movido: $($_.Name) para $pastaAlvo"
    }
}

Neste exemplo, usamos um dicionário para mapear extensões às pastas de destino. O loop ForEach-Object processa cada arquivo, verifica se a extensão está no mapa e move o arquivo para a pasta correspondente. O parâmetro -Force sobrescreve arquivos existentes, se necessário, e o Write-Host fornece feedback visual.

Para tornar o script mais flexível, você pode solicitar as pastas de origem e destino como parâmetros, tornando-o reutilizável em diferentes cenários.

Renomeação

Renomear arquivos em lote é outra tarefa que pode ser automatizada com facilidade. O cmdlet Rename-Item permite alterar nomes individualmente, mas quando combinado com loops e lógica, podemos criar padrões de renomeação complexos.

Suponha que você tenha uma série de fotos com nomes como "IMG_001.jpg", "IMG_002.jpg" e queira prefixá-las com a data de hoje. O script a seguir adiciona o prefixo "2024-12-25_" a todos os arquivos .jpg da pasta atual.

$prefixo = Get-Date -Format "yyyy-MM-dd"
Get-ChildItem -Path . -Filter "*.jpg" -File | ForEach-Object {
    $novoNome = "$prefixo_$($_.Name)"
    Rename-Item -Path $_.FullName -NewName $novoNome
    Write-Host "Renomeado: $($_.Name) para $novoNome"
}

Outro cenário comum é substituir partes do nome, como remover espaços ou caracteres especiais. O PowerShell oferece o operador -replace para substituições baseadas em regex. Por exemplo, para substituir espaços por underscores em todos os arquivos .txt:

Get-ChildItem -Path . -Filter "*.txt" -File | ForEach-Object {
    $novoNome = $_.Name -replace '\s+', '_'
    Rename-Item -Path $_.FullName -NewName $novoNome
}

É importante validar se o novo nome não entra em conflito com um arquivo existente. Podemos adicionar uma verificação com Test-Path antes de renomear:

Get-ChildItem -Path . -Filter "*.txt" -File | ForEach-Object {
    $novoNome = $_.Name -replace '\s+', '_'
    $caminhoNovo = Join-Path $_.DirectoryName $novoNome
    if (-not (Test-Path $caminhoNovo)) {
        Rename-Item -Path $_.FullName -NewName $novoNome
    } else {
        Write-Warning "Não foi possível renomear $($_.Name): destino já existe."
    }
}

Essas técnicas são úteis para padronizar nomes de arquivos em projetos, downloads ou backups.

Limpeza

A limpeza de arquivos antigos ou temporários é uma tarefa de manutenção importante. O PowerShell pode excluir arquivos com base na idade, tamanho ou outros atributos. O cmdlet Remove-Item é usado para excluir, mas devemos ter cuidado para não apagar arquivos importantes.

O exemplo a seguir exclui todos os arquivos de uma pasta que são mais antigos que 30 dias. Usamos Get-ChildItem com o parâmetro -Recurse para incluir subpastas e Where-Object para filtrar pela data de criação ou última escrita.

$pasta = "C:\Temp"
$dias = 30
$dataLimite = (Get-Date).AddDays(-$dias)

Get-ChildItem -Path $pasta -Recurse -File | Where-Object {
    $_.LastWriteTime -lt $dataLimite
} | Remove-Item -Force -Verbose

O parâmetro -Verbose exibe detalhes sobre cada arquivo excluído, o que ajuda a auditar a operação. Para evitar exclusões acidentais, você pode usar o parâmetro -WhatIf para simular a exclusão sem realmente apagar:

Get-ChildItem -Path $pasta -Recurse -File | Where-Object {
    $_.LastWriteTime -lt $dataLimite
} | Remove-Item -Force -WhatIf

Além de idade, podemos filtrar por tamanho, extensão ou até mesmo por nome. Por exemplo, para excluir arquivos .tmp que tenham mais de 1 GB:

Get-ChildItem -Path $pasta -Recurse -File -Filter "*.tmp" | Where-Object {
    $_.Length -gt 1GB -and $_.LastWriteTime -lt $dataLimite
} | Remove-Item -Force

É sempre recomendável criar um log das exclusões para referência futura. Você pode redirecionar a saída para um arquivo texto ou usar Write-Output e depois redirecionar.

Relatórios

Gerar relatórios sobre arquivos e pastas é útil para monitoramento e auditoria. O PowerShell permite criar relatórios em formato texto, CSV ou HTML, dependendo da necessidade. Vamos ver como gerar um relatório de todos os arquivos de uma pasta com informações como nome, tamanho, data de modificação e atributos.

O exemplo abaixo cria um relatório em CSV com os arquivos de uma pasta específica, incluindo o caminho completo, tamanho em KB e data de última modificação.

$pasta = "C:\Documentos"
$relatorio = "C:\Relatorios\documentos.csv"

Get-ChildItem -Path $pasta -Recurse -File | Select-Object FullName, Length, LastWriteTime |
    Export-Csv -Path $relatorio -NoTypeInformation -Encoding UTF8
Write-Host "Relatório gerado em $relatorio"

O cmdlet Export-Csv converte a saída em formato CSV, que pode ser aberto no Excel ou em outros programas. Para gerar um relatório em HTML, podemos usar o cmdlet ConvertTo-Html e salvar o resultado em um arquivo .html:

$pasta = "C:\Documentos"
$arquivos = Get-ChildItem -Path $pasta -Recurse -File | Select-Object Name, Length, LastWriteTime
$html = $arquivos | ConvertTo-Html -Title "Relatório de Documentos" -PreContent "

Relatório de Arquivos

" $html | Out-File -FilePath "C:\Relatorios\documentos.html" -Encoding UTF8

Além de listar arquivos, podemos criar relatórios de uso de espaço em disco. O script a seguir calcula o tamanho total de cada subpasta e gera um resumo:

$pasta = "C:\Dados"
$relatorio = Get-ChildItem -Path $pasta -Directory | ForEach-Object {
    $tamanho = (Get-ChildItem -Path $_.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
    [PSCustomObject]@{
        Pasta = $_.Name
        TamanhoMB = [math]::Round($tamanho / 1MB, 2)
    }
}
$relatorio | Format-Table -AutoSize

Esse relatório pode ser exportado para CSV ou HTML da mesma forma que os exemplos anteriores. Relatórios são valiosos para identificar pastas que consomem muito espaço ou para verificar se as políticas de retenção estão sendo cumpridas.

Boas Práticas e Observações Finais

Ao automatizar tarefas de arquivos, é fundamental adotar boas práticas para evitar problemas. Sempre teste seus scripts em um ambiente controlado antes de executar em produção. Use os parâmetros -WhatIf e -Confirm para revisar as ações. Crie logs das operações para auditoria. E, acima de tudo, faça backup dos dados antes de executar scripts que movem, renomeiam ou excluem arquivos.

Outra recomendação é usar funções para encapsular a lógica e tornar os scripts mais modulares. Por exemplo, você pode criar uma função Move-FilesByExtension que reutiliza o código de organização em lote. Isso facilita a manutenção e a reutilização.

Lembre-se de que o PowerShell é case-insensitive para comandos e parâmetros, mas é uma boa prática manter a consistência na escrita. Use comentários para documentar o propósito do script e as decisões importantes.

Referências

Exercícios

  1. Organização em lote: Escreva um script que mova todos os arquivos .pdf de uma pasta de origem para uma subpasta chamada "PDFs", criando a subpasta se necessário. Teste com -WhatIf antes de executar.

✓ Resposta:
$origem = "C:\Origem"
$destino = "C:\Origem\PDFs"
if (-not (Test-Path $destino)) {
    New-Item -ItemType Directory -Path $destino | Out-Null
}
Get-ChildItem -Path $origem -Filter "*.pdf" -File | Move-Item -Destination $destino -WhatIf
  1. Renomeação: Crie um script que adicione o sufixo "_backup" a todos os arquivos .txt de uma pasta, antes da extensão (por exemplo, "nota.txt" vira "nota_backup.txt").

✓ Resposta:
Get-ChildItem -Path . -Filter "*.txt" -File | ForEach-Object {
    $novoNome = $_.BaseName + "_backup" + $_.Extension
    Rename-Item -Path $_.FullName -NewName $novoNome
}
  1. Limpeza: Escreva um script que exclua todos os arquivos .log de uma pasta que tenham mais de 7 dias. Use -WhatIf para simular antes de executar.

✓ Resposta:
$pasta = "C:\Logs"
$dataLimite = (Get-Date).AddDays(-7)
Get-ChildItem -Path $pasta -Filter "*.log" -File | Where-Object {
    $_.LastWriteTime -lt $dataLimite
} | Remove-Item -WhatIf
  1. Relatórios: Gere um relatório CSV com todos os arquivos .png de uma pasta, incluindo nome, tamanho em KB e data de criação. Salve em "relatorio_png.csv".

✓ Resposta:
Get-ChildItem -Path . -Filter "*.png" -File | Select-Object Name, @{Name="TamanhoKB";Expression={[math]::Round($_.Length/1KB,2)}}, CreationTime |
    Export-Csv -Path "relatorio_png.csv" -NoTypeInformation -Encoding UTF8
  1. Combinação: Crie um script que mova todos os arquivos de uma pasta para subpastas baseadas no ano de modificação (por exemplo, arquivos de 2023 vão para "2023", de 2024 para "2024"). Use Get-ChildItem e Move-Item.

✓ Resposta:
$pastaOrigem = "C:\Origem"
Get-ChildItem -Path $pastaOrigem -File | ForEach-Object {
    $ano = $_.LastWriteTime.Year
    $destino = Join-Path $pastaOrigem $ano
    if (-not (Test-Path $destino)) {
        New-Item -ItemType Directory -Path $destino | Out-Null
    }
    Move-Item -Path $_.FullName -Destination $destino
}