Linux · 40 min

Automatize uma Tarefa com Shell Script

Transforme os comandos que você aprendeu num script Bash real e reutilizável: argumentos, condicionais, loops e um cabeçalho seguro — construindo um resumidor de logs que transforma um arquivo de log cru num relatório. O complemento prático do Terminal & Linux Essentials.

Problema

Digitar os mesmos três comandos todo dia é como você sabe que uma tarefa deveria ser um script. A distância entre "sei usar o terminal" e "automatizo meu trabalho" é pequena — um shebang, algumas variáveis, um `if` e um `for` — mas é o salto que te transforma de usuário de ferramenta em construtor de ferramenta. Neste lab você vai escrever um script Bash real do zero: um **resumidor de logs** que recebe um arquivo de log, conta erros e avisos, acha as fontes que mais causam problema e imprime um relatório limpo. No caminho você vai tratar argumentos, se proteger de entrada ruim, iterar sobre linhas e adicionar o cabeçalho de segurança que todo script sério precisa. Você vai terminar com um script que poderia de fato colocar no crontab de um servidor.

Objetivos

Pré-requisitos

O que você vai construir

Um único script, logstats.sh, que você vai crescer passo a passo. Dado um arquivo de log, ele vai:

Cada passo adiciona um conceito de shell e uma funcionalidade ao script, então você termina com algo
real em vez de trechos de brinquedo.

Por que script, e não só comandos

Um comando que você digita some no momento em que sai da tela. Um script é esse conhecimento
capturado — versionado, compartilhável, agendável. O mesmo grep | sort | uniq -c que você
rodaria na mão vira, embrulhado num script, um relatório que qualquer um (ou o cron) roda
identicamente às 2 da manhã. Essa reprodutibilidade é todo o valor da automação.

Um arquivo de log de exemplo

Você vai gerar um logfile realista no Passo 1 para todo mundo trabalhar nos mesmos dados. Logs reais
são mais bagunçados, mas o formato — um timestamp, um nível, uma fonte, uma mensagem por linha — é universal.

Como trabalhar neste lab

Construa o script incrementalmente e rode-o após cada passo (./logstats.sh sample.log), lendo a
saída real. Não cole tudo só no fim — o ponto é ver cada peça funcionar.

Passos

  1. Do comando ao script: shebang e chmod

    Primeiro, crie dados de exemplo e seu primeiro script executável.

    Gere um logfile de exemplo

    Cole isto para criar sample.log com uma mistura realista de níveis e fontes:

    cat > sample.log <<'EOF'
    2026-07-10 09:01:12 INFO  auth      user login ok
    2026-07-10 09:02:03 WARN  auth      slow response
    2026-07-10 09:02:44 ERROR payments  gateway timeout
    2026-07-10 09:03:10 INFO  web       GET /home 200
    2026-07-10 09:04:55 ERROR payments  card declined
    2026-07-10 09:05:01 WARN  web       deprecated endpoint
    2026-07-10 09:06:22 ERROR auth      token expired
    2026-07-10 09:07:30 INFO  web       GET /pricing 200
    2026-07-10 09:08:14 ERROR payments  gateway timeout
    EOF
    wc -l sample.log      # 9 linhas
    

    Escreva o primeiro script

    Crie logstats.sh:

    #!/usr/bin/env bash
    echo "logstats starting"
    wc -l "sample.log"
    
    • A primeira linha é o shebang: #!/usr/bin/env bash diz ao SO para rodar o arquivo com o
      Bash. Sem ele, o arquivo é só texto.
    • Usar env bash acha o Bash no PATH do usuário, o que é mais portátil do que fixar /bin/bash.

    Torne executável e rode

    chmod +x logstats.sh    # adiciona a permissão de execução
    ./logstats.sh           # o ./ diz "rode o arquivo aqui", não um comando do PATH
    

    Um arquivo não é executável só porque contém comandos — ele precisa do bit de execução. É
    isso que o chmod +x concede; ls -l logstats.sh agora mostra um x nas permissões.

    Entregável deste passo: a saída de ls -l logstats.sh mostrando o bit x, e a saída do script.

  2. Argumentos e validação

    Fixar sample.log faz um script de um truque só. Receba o nome do arquivo como argumento.

    Leia o argumento

    Substitua o corpo do logstats.sh:

    #!/usr/bin/env bash
    
    logfile="$1"        # primeiro argumento passado na linha de comando
    echo "Analisando: $logfile"
    wc -l "$logfile"
    
    • $1 é o primeiro argumento, $2 o segundo, e assim por diante. $@ é todos os argumentos,
      $# é quantos são.
    • Sempre coloque variáveis entre aspas ("$logfile") para nomes com espaços não quebrarem o script.

    Rode de duas formas:

    ./logstats.sh sample.log
    ./logstats.sh              # sem argumento — o que acontece?
    

    Sem argumento, $1 está vazio e o wc dá um erro feio. Um script real valida primeiro.

    Proteja a entrada

    Adicione uma checagem no topo, antes de qualquer trabalho:

    #!/usr/bin/env bash
    
    if [ "$#" -lt 1 ]; then
      echo "Uso: $0 <logfile>" >&2
      exit 1
    fi
    
    logfile="$1"
    
    if [ ! -f "$logfile" ]; then
      echo "Erro: '$logfile' não é um arquivo legível" >&2
      exit 2
    fi
    
    echo "Analisando: $logfile"
    

    O que cada peça faz:

    • [ "$#" -lt 1 ] → "menos de 1 argumento". Se for o caso, imprime o uso no stderr (>&2) e sai com código diferente de zero.
    • $0 é o próprio nome do script — uma mensagem de uso autodocumentada.
    • [ ! -f "$logfile" ] → "não é um arquivo regular". Falhe cedo com um código de saída distinto.
    • Códigos de saída diferentes (1 para uso, 2 para arquivo ruim) deixam outros scripts reagirem ao porquê da falha.

    Entregável deste passo: a saída ao rodar sem argumento e com um arquivo inexistente, mostrando as mensagens de erro e (via echo $?) os códigos de saída.

  3. Condicionais e contagem

    Agora o trabalho de verdade: contar o que há no log e ramificar conforme o resultado.

    Conte os níveis

    Adicione abaixo da validação:

    total=$(wc -l < "$logfile")
    errors=$(grep -c 'ERROR' "$logfile")
    warnings=$(grep -c 'WARN' "$logfile")
    
    echo "Total de linhas: $total"
    echo "Erros:           $errors"
    echo "Avisos:          $warnings"
    
    • $( ... ) é substituição de comando: roda o comando e captura a saída na variável.
    • wc -l < "$logfile" alimenta o arquivo via stdin para o wc imprimir só o número (sem o nome do arquivo).
    • grep -c PADRÃO conta linhas que casam diretamente — sem precisar de pipe para o wc.

    Ramifique conforme as contagens

    Transforme números num veredito com if/elif/else:

    if [ "$errors" -eq 0 ]; then
      echo "Status: saudável ✅"
    elif [ "$errors" -lt 3 ]; then
      echo "Status: atenção ⚠️  ($errors erros)"
    else
      echo "Status: crítico ❌ ($errors erros)"
    fi
    

    Comparações de número usam operadores de palavra: -eq (=), -lt (<), -gt (>), -le, -ge.
    (Comparações de string usam = e != — uma pegadinha clássica.)

    Rode ./logstats.sh sample.log — com 4 erros na amostra, você deve cair no ramo crítico.

    Entregável deste passo: a saída do script mostrando as três contagens e o veredito de status.

  4. Loops e agregação: principais fontes de erro

    Uma contagem é útil; de onde os erros vêm é acionável. Agregue por fonte.

    O pipeline que acha os maiores culpados

    A terceira coluna de cada linha de log é a fonte (auth, payments, web). Extraia, agrupe e ranqueie:

    echo "Principais fontes de erro:"
    grep 'ERROR' "$logfile" | awk '{ print $4 }' | sort | uniq -c | sort -rn
    

    Leia o pipeline da esquerda para a direita — essa é a essência do Unix:

    • grep 'ERROR' → mantém só as linhas de erro.
    • awk '{ print $4 }' → imprime o 4º campo separado por espaço (a fonte). O awk separa por espaço automaticamente.
    • sort → agrupa fontes idênticas (necessário antes do uniq).
    • uniq -c → colapsa duplicatas e prefixa cada uma com sua contagem.
    • sort -rn → ordena por essa contagem, numericamente (-n), da maior primeiro (-r).

    Na amostra, payments deve liderar com 3 erros.

    Itere sobre os resultados

    Para formatar cada linha de fonte você mesmo, itere com while read:

    echo "Detalhamento:"
    grep 'ERROR' "$logfile" | awk '{ print $4 }' | sort | uniq -c | sort -rn |
    while read -r count source; do
      echo "  - $source causou $count erro(s)"
    done
    
    • while read -r count source lê cada linha, separando em duas variáveis no espaço.
    • -r impede que barras invertidas sejam interpretadas — sempre use com read.
    • O corpo do loop roda uma vez por fonte, deixando você formatar, aplicar limite, ou alertar por linha.

    Entregável deste passo: a saída de "Principais fontes de erro" e "Detalhamento" para o log de exemplo.

  5. Torne robusto: set -euo pipefail e uma função de relatório

    Um script que continua após um erro pode causar estrago de verdade. Adicione o cabeçalho de segurança e estrutura.

    O cabeçalho de segurança

    Coloque isto logo após o shebang:

    #!/usr/bin/env bash
    set -euo pipefail
    

    Cada flag previne uma classe de bug silencioso:

    • -e → sai imediatamente se algum comando falhar, em vez de seguir tropeçando.
    • -u → erro em variáveis indefinidas (um $logfyle com typo vira um crash, não string vazia).
    • -o pipefail → um pipeline falha se qualquer estágio falhar, não só o último.

    Esse trio é o hábito mais importante para scripts confiáveis. Teste: referencie temporariamente
    uma variável indefinida e veja o script parar em vez de produzir lixo.

    Embrulhe o relatório numa função

    Agrupe a saída numa função para legibilidade e reuso:

    print_report() {
      local file="$1"
      echo "=============================="
      echo " Relatório de log: $file"
      echo " Gerado em:        $(date '+%Y-%m-%d %H:%M')"
      echo "=============================="
      echo "Total de linhas: $(wc -l < "$file")"
      echo "Erros:           $(grep -c 'ERROR' "$file")"
      echo "Avisos:          $(grep -c 'WARN'  "$file")"
    }
    
    print_report "$logfile"
    
    • local file="$1" escopa a variável à função — ela não vaza para o resto do script.
    • Funções transformam um script longo em peças nomeadas e testáveis, exatamente como funções em qualquer linguagem.

    Rode o script completo mais uma vez e confirme que o relatório renderiza limpo, com cabeçalho e contagens.

    Entregável deste passo: a saída do relatório completo, e uma nota sobre o que aconteceu quando você disparou o set -u com uma variável indefinida.

  6. Entregue: seu script e uma execução de exemplo

    Empacote o script, o log de exemplo e uma execução capturada, e entregue.

    Monte o repositório

    Seu logstats.sh deve agora conter, em ordem: o shebang, set -euo pipefail, validação de
    argumento, a função print_report, o pipeline/loop das principais fontes de erro, e o veredito
    de status. Crie um SUBMISSION.md capturando uma execução real:

    # Lab de Shell Script — <seu nome>
    
    ## O script
    Veja logstats.sh (versão final).
    
    ## Execução de exemplo
    $ ./logstats.sh sample.log
    <cole a saída real completa: cabeçalho do relatório, contagens, status, principais fontes>
    
    ## Comportamento de validação
    $ ./logstats.sh            # (erro de uso, exit 1)
    $ ./logstats.sh nope.log   # (arquivo ruim, exit 2)
    <cole ambos, com `echo $?` após cada>
    
    ## Reflexão (2-3 frases)
    Nas minhas palavras: contra o que o `set -euo pipefail` protege, e uma tarefa
    da minha própria vida que eu poderia automatizar com um script assim.
    

    Entregue

    git init
    git add logstats.sh sample.log SUBMISSION.md
    git commit -m "Lab de shell script — resumidor de logs"
    # faça push para um repo público ou gist, depois entregue essa URL
    

    Critério de submissão (autoverificação)

    • logstats.sh começa com um shebang e set -euo pipefail
    • Valida a quantidade de argumentos e a existência do arquivo, com códigos de saída distintos e diferentes de zero
    • Usa substituição de comando $(...) para contar linhas/erros/avisos
    • Usa um if/elif/else para imprimir um veredito de status
    • Agrega as principais fontes de erro com grep | awk | sort | uniq -c | sort -rn
    • Um loop while read ou uma função é usado para estruturar a saída
    • A entrega mostra uma execução real mais os dois casos de falha de validação com seus códigos de saída

    O que vem a seguir

    Você já consegue transformar qualquer tarefa repetitiva do terminal num script seguro e
    reutilizável. Um próximo passo natural é agendá-lo (cron) e, no Backend Developer Path, usar
    scripts assim para automatizar partes de construir e fazer deploy da API que você vai criar.