Bem-vindo à aula 96 do nosso curso de Shell Script! Hoje vamos colocar em prática tudo o que aprendemos até aqui, construindo um projeto completo: um gerador de relatórios. Este projeto vai integrar coleta de dados com grep/awk, formatação de saída, geração de arquivos e até agendamento automático com cron. Ao final, você terá uma ferramenta útil para gerar relatórios de sistema, como uso de disco, memória e processos.

Vamos abordar cada etapa do desenvolvimento, desde a coleta bruta de dados até a entrega de um relatório bem formatado. Você vai ver como combinar comandos poderosos do Linux para criar soluções profissionais. Prepare-se para sujar as mãos com código!

Coleta com awk/grep

A primeira etapa de qualquer relatório é coletar os dados brutos. No Linux, temos comandos como df, free, ps e uptime que fornecem informações valiosas. No entanto, esses comandos geralmente produzem saídas com muitas colunas e informações que nem sempre precisamos. É aqui que entram o grep e o awk.

O grep é usado para filtrar linhas que correspondem a um padrão. Por exemplo, para listar apenas partições do tipo ext4 no df, usamos df -h | grep ext4. Já o awk é uma ferramenta mais poderosa, permitindo processar colunas e linhas de forma estruturada. Com ele, podemos extrair campos específicos, fazer cálculos e até formatar a saída. Vamos ver um exemplo prático de coleta:

#!/bin/bash
# Coletando uso de disco (df -h) e filtrando por sistemas de arquivos ext4
# Usamos grep para selecionar linhas e awk para extrair colunas
df -h | grep '/dev/' | awk '{print $1, $5, $6}'

Nesse exemplo, grep '/dev/' filtra as linhas que contêm dispositivos em /dev, e awk imprime a primeira coluna (sistema de arquivos), a quinta (uso percentual) e a sexta (ponto de montagem). Essa é a base da coleta: extrair somente o que interessa.

Para memória, podemos usar free -m e extrair as linhas de Memória e Swap. Já para processos, ps aux é uma mina de ouro, mas com muitas colunas; podemos usar awk para selecionar as que precisamos, como PID, CPU, MEM e comando. A coleta é o alicerce do relatório, então capriche nos filtros.

Formatação

Depois de coletar os dados, precisamos formatá-los de forma legível e profissional. O awk é excelente para isso, pois permite usar printf para alinhar colunas, definir larguras e até adicionar cores (se o terminal suportar). Vamos criar uma função que formata a saída do disco:

#!/bin/bash
# Função para formatar uso de disco
formatar_disco() {
    echo "Sistema de Arquivos | Uso % | Montado em"
    echo "----------------------------------------"
    df -h | grep '/dev/' | awk '{printf "%-20s %-5s %s\n", $1, $5, $6}'
}

No printf, %-20s alinha à esquerda com 20 caracteres, %-5s com 5 caracteres, e assim por diante. Isso garante que as colunas fiquem alinhadas, mesmo que os valores tenham tamanhos diferentes. Também podemos usar cores com códigos ANSI, como \033[31m para vermelho em casos de uso acima de 90%.

Além do disco, podemos formatar a memória e os processos. Por exemplo, para a memória, queremos mostrar total, usado e livre em MB, com uma barra de progresso visual. Para processos, podemos ordenar por uso de CPU e limitar a 10 linhas. A formatação é o que torna o relatório agradável e útil.

Vamos também adicionar um cabeçalho com data e hora, usando date. Isso dá contexto ao relatório. A formatação é uma arte: deve ser clara, concisa e informativa.

Saída

A saída de um relatório pode ser exibida no terminal, salva em um arquivo de texto ou até gerada em HTML para visualização em navegador. Vamos implementar as três opções no nosso script, usando parâmetros de linha de comando. Por exemplo, ./relatorio.sh -t exibe no terminal, ./relatorio.sh -f relatorio.txt salva em arquivo, e ./relatorio.sh -h gera HTML.

#!/bin/bash
# Gerador de relatórios

# Função para gerar saída em texto (terminal ou arquivo)
gerar_texto() {
    echo "Relatório do Sistema - $(date)"
    echo "================================"
    echo ""
    echo "## Uso de Disco"
    formatar_disco
    echo ""
    echo "## Memória"
    formatar_memoria
    echo ""
    echo "## Top 10 Processos por CPU"
    formatar_processos
}

# Função para gerar HTML
gerar_html() {
    echo "<html><head><title>Relatório</title></head><body>"
    echo "<h1>Relatório do Sistema - $(date)</h1>"
    echo "<h2>Uso de Disco</h2><pre>"
    formatar_disco
    echo "</pre><h2>Memória</h2><pre>"
    formatar_memoria
    echo "</pre><h2>Top Processos</h2><pre>"
    formatar_processos
    echo "</pre></body></html>"
}

# Processamento dos argumentos
while getopts "tf:h" opt; do
    case $opt in
        t) gerar_texto ;;
        f) gerar_texto > "$OPTARG" ;;
        h) gerar_html > relatorio.html ;;
        *) echo "Uso: $0 [-t] [-f arquivo] [-h]" ;;
    esac
done

No caso da saída em arquivo, redirecionamos a saída da função para o arquivo. Para HTML, precisamos escapar os caracteres especiais, mas como estamos gerando apenas texto simples dentro de tags <pre>, é suficiente. O importante é que a saída seja flexível e atenda a diferentes necessidades.

Podemos também adicionar compressão com gzip para arquivos grandes, ou envio por e-mail com mail, mas isso fica como extensão. A saída é a entrega do relatório, então deve ser robusta.

Agendamento

Um relatório só é útil se for gerado regularmente. No Linux, o cron é o agendador padrão. Podemos adicionar uma entrada no crontab para executar nosso script diariamente em um horário específico. Por exemplo, para gerar um relatório às 6h da manhã e salvar em um arquivo, adicionamos:

# Abrir crontab
crontab -e

# Adicionar linha (executa todo dia às 6h)
0 6 * * * /caminho/para/relatorio.sh -f /caminho/para/relatorio_$(date +\%Y\%m\%d).txt

Note que usamos $(date +\%Y\%m\%d) para incluir a data no nome do arquivo. No crontab, o % deve ser escapado com \. Mas podemos simplificar criando um script wrapper que gera o nome do arquivo internamente. Vamos criar um script gerar_relatorio_diario.sh que chama nosso script principal com o nome do arquivo:

#!/bin/bash
# Script para gerar relatório diário com data no nome
DATA=$(date +%Y%m%d)
/caminho/para/relatorio.sh -f /caminho/para/relatorios/relatorio_$DATA.txt

Depois, no crontab, apenas chamamos esse wrapper: 0 6 * * * /caminho/para/gerar_relatorio_diario.sh. Isso evita problemas com o escape.

Outra opção é usar systemd-timers, mas o cron é mais simples e universal. Lembre-se de que o cron não carrega as variáveis de ambiente do usuário, então é recomendável usar caminhos absolutos e definir PATH no início do script. Agendamento é o que torna o relatório verdadeiramente útil: ele aparece sem você precisar lembrar.

Projeto completo

Vamos juntar todas as partes em um script final. Ele terá funções para coletar, formatar e gerar saída, além de suporte a agendamento. Aqui está uma versão completa:

#!/bin/bash

# Configurações
ARQUIVO_SAIDA=""
FORMATO="texto"
DATA=$(date "+%d/%m/%Y %H:%M")

# Funções de coleta e formatação
formatar_disco() {
    echo "Dispositivo          Uso% Montado em"
    echo "----------------------------------"
    df -h | grep '/dev/' | awk '{printf "%-20s %-4s %s\n", $1, $5, $6}'
}

formatar_memoria() {
    echo "Memória (MB):"
    free -m | awk 'NR==1{printf "%-10s %8s %8s %8s\n", $1, $2, $3, $4} NR==2{printf "%-10s %8s %8s %8s\n", $1, $2, $3, $4}'
}

formatar_processos() {
    echo "Top 5 processos por CPU:"
    ps aux --sort=-%cpu | head -6 | awk '{printf "%-8s %-5s %-5s %s\n", $2, $3, $4, $11}'
}

# Geração de saída
gerar_texto() {
    echo "Relatório do Sistema - $DATA"
    echo "============================"
    echo
    echo "## Disco"
    formatar_disco
    echo
    echo "## Memória"
    formatar_memoria
    echo
    echo "## Processos"
    formatar_processos
}

gerar_html() {
    echo "<html>"
    echo "<head><title>Relatório</title></head>"
    echo "<body>"
    echo "<h1>Relatório do Sistema - $DATA</h1>"
    echo "<h2>Disco</h2><pre>"
    formatar_disco
    echo "</pre><h2>Memória</h2><pre>"
    formatar_memoria
    echo "</pre><h2>Processos</h2><pre>"
    formatar_processos
    echo "</pre>"
    echo "</body></html>"
}

# Processamento de argumentos
while getopts "tf:h" opt; do
    case $opt in
        t) FORMATO="texto" ;;
        f) ARQUIVO_SAIDA="$OPTARG" ;;
        h) FORMATO="html" ;;
        *) echo "Uso: $0 [-t] [-f arquivo] [-h]" >&2; exit 1 ;;
    esac
done

# Geração final
if [ "$FORMATO" = "html" ]; then
    gerar_html
else
    gerar_texto
fi | {
    if [ -n "$ARQUIVO_SAIDA" ]; then
        cat > "$ARQUIVO_SAIDA"
        echo "Relatório salvo em $ARQUIVO_SAIDA"
    else
        cat
    fi
}

Esse script é funcional e pronto para uso. Você pode adaptá-lo para incluir mais métricas, como rede ou logs. O importante é entender a arquitetura: coleta, formatação, saída e agendamento.

Boas práticas

Ao desenvolver scripts de relatório, siga estas boas práticas:

  • Use funções para modularizar o código, facilitando manutenção.
  • Valide a existência de comandos antes de usá-los (ex.: command -v df).
  • Trate erros com set -e ou checagens manuais.
  • Use caminhos absolutos em scripts agendados.
  • Adicione comentários para que outros (e você mesmo no futuro) entendam o código.
  • Teste em um ambiente seguro antes de colocar em produção.

Essas práticas garantem que seu script seja robusto e confiável.

Referências

Exercícios

  1. Crie um script que colete o uso de memória usando free -m e exiba apenas as linhas de Memória e Swap, formatadas com awk para mostrar total, usado e livre.
  2. ✓ Resposta:
    #!/bin/bash
    free -m | awk '/Mem|Swap/ {print $1": Total="$2" Usado="$3" Livre="$4}'
    
  3. Escreva um comando awk que, a partir do df -h, liste apenas partições com uso acima de 80%, mostrando o ponto de montagem e a porcentagem.
  4. ✓ Resposta:
    df -h | awk 'NR>1 && +$5 > 80 {print $6, $5}'
    
  5. Modifique o script do projeto para aceitar um parâmetro -p que liste os processos ordenados por uso de memória em vez de CPU.
  6. ✓ Resposta:
    # Adicione no case do getopts:
    p) FORMATO="processos_mem" ;;
    # E na função gerar_texto, adicione:
    if [ "$FORMATO" = "processos_mem" ]; then
        ps aux --sort=-%mem | head -6 | awk '{printf "%-8s %-5s %-5s %s\n", $2, $3, $4, $11}'
    fi
    
  7. Configure um cron job para executar o script todos os dias às 22h, gerando um relatório em HTML com o nome relatorio_diario.html.
  8. ✓ Resposta:
    crontab -e
    # Adicione:
    0 22 * * * /caminho/para/relatorio.sh -h > /caminho/para/relatorio_diario.html
    
  9. Escreva uma função que adicione uma linha de resumo ao final do relatório, mostrando o total de disco usado e a média de uso de CPU.
  10. ✓ Resposta:
    resumo() {
        echo "Resumo:"
        echo "- Uso total de disco: $(df -h | grep '/dev/' | awk '{s+=$3} END {print s}') GB"
        echo "- Média de CPU: $(ps aux | awk 'NR>1 {sum+=$3} END {printf "%.1f%%", sum/(NR-1)}')"
    }