Mão na massa · ~49 min
Construindo um script de monitoramento, peça por peça
Hoje você não escreve vários scripts soltos — escreve um só, que cresce a cada passo. No fim, ele verifica o disco, decide se há problema, registra em log e avisa. É o mesmo tipo de ferramenta que roda de madrugada em servidores reais.
📖 Este roteiro é a leitura obrigatória da semana
Nenhum dos dois livros-base da disciplina cobre shell script. Por isso, nesta semana, este documento substitui o capítulo de livro — e o quiz cobra o conteúdo dele.
Leia as explicações, não só os comandos. Os blocos "o que é" e "por que importa" são o conteúdo teórico da semana.
Passo 1 de 7
O primeiro script que roda
nano ~/monitor.shO que é
A primeira linha, #!/bin/bash, é o shebang: diz ao sistema qual programa deve interpretar este arquivo. Sem ela, o SO não sabe que é um script bash.
Por que importa
É o que transforma um arquivo de texto qualquer num programa executável. Nenhuma compilação é necessária — o shell já é o interpretador.
#!/bin/bash echo "Monitor de sistema" echo "Executado por: $USER"
salve com Ctrl+O, Enter, Ctrl+X. Depois rode chmod +x ~/monitor.sh e execute com ~/monitor.sh. O $USER é uma variável que o próprio sistema já define com seu nome de usuário.
Deu "Permission denied"
Faltou o chmod +x ~/monitor.sh. Esse é o erro mais comum de quem começa: o arquivo existe e o conteúdo está certo, mas sem a permissão de execução o sistema se recusa a rodá-lo. Confira com ls -l ~/monitor.sh — devem aparecer x nas permissões.
Passo 2 de 7
Variáveis e a captura da saída de um comando
Acrescente ao monitor.shO que é
VAR="valor" atribui — sem espaços em volta do =. $(comando) executa o comando e devolve a saída dele, que pode ser guardada numa variável.
Por que importa
É o que permite o script reagir ao estado real da máquina, em vez de executar sempre a mesma coisa cegamente.
#!/bin/bash LIMITE=80 DATA=$(date +%F) USO=$(df / | tail -1 | awk '{print $5}' | tr -d '%') echo "Data: $DATA" echo "Uso do disco: ${USO}%" echo "Limite configurado: ${LIMITE}%"
o script imprime a data de hoje e o percentual real de uso do seu disco. A linha do USO é um pipeline do Encontro 6: pega a saída do df, fica com a última linha, extrai a 5ª coluna e remove o símbolo % — sobrando só o número.
Por que as chaves em ${USO}%?
As chaves delimitam onde o nome da variável termina. Sem elas, $USO% ainda funcionaria neste caso, mas $USOpercent faria o shell procurar uma variável chamada USOpercent, que não existe. Usar ${} sempre que houver texto colado é um bom hábito.
Erro: "LIMITE: command not found"
Você deixou espaço em volta do =. Em bash, LIMITE = 80 é interpretado como "execute o comando LIMITE com os argumentos = e 80". Escreva sempre LIMITE=80, sem espaço algum.
o que a construção $(...) faz, e por que ela é essencial para um script de monitoramento? Use a linha do USO como exemplo.
0 / 40 caracteres
Passo 3 de 7
Fazendo o script decidir
Acrescente a condicionalO que é
if [ condição ]; then ... else ... fi. Os espaços dentro dos colchetes são obrigatórios, e -gt significa "maior que" para números.
Por que importa
Sem condicional, um monitor não monitora — ele apenas imprime. É o if que transforma dado em decisão.
if [ "$USO" -gt "$LIMITE" ]; then echo "ALERTA: disco em ${USO}%, acima do limite de ${LIMITE}%" else echo "OK: disco em ${USO}%, dentro do limite" fi
provavelmente a mensagem "OK". Para testar o outro caminho, baixe temporariamente o limite (LIMITE=10) e rode de novo — o script deve passar a alertar. Depois volte para 80.
Por que as aspas em "$USO" e "$LIMITE"?
Sem aspas, uma variável vazia desaparece da linha, e uma com espaços vira várias palavras. O caso mais traiçoeiro é o teste de texto: com NOME vazio, [ -n $NOME ] vira [ -n ], que dá verdadeiro — o script conclui que há um nome quando não há. Com aspas, [ -n "$NOME" ] vira [ -n "" ], que dá falso, como deveria. Sempre use aspas em volta de variáveis — é a regra que evita mais bugs em shell script.
Atenção a um limite da regra: no teste numérico, aspas não bastam. Se USO vier vazio, tanto [ $USO -gt 80 ] quanto [ "$USO" -gt "80" ] imprimem um erro, a condição fica falsa e o script cai no else — dizendo "OK" sem ter medido nada. Por isso um monitor sério confere antes se o valor é mesmo um número (é o desafio do fim do roteiro).
Erro: "[: missing ]" ou similar
Faltou espaço dentro dos colchetes. [$USO -gt $LIMITE] está errado; o certo é [ "$USO" -gt "$LIMITE" ], com espaço depois do [ e antes do ]. O [ é, na verdade, um comando — e comandos precisam ser separados dos argumentos por espaço.
Passo 4 de 7
Repetindo para vários itens
for aplica uma sequência inteira de comandos a cada item de uma lista — indo além do que o coringa do Encontro 12 permitia.Acrescente o laçoO que é
for VAR in lista; do ... done. A cada volta, VAR assume o valor do item seguinte da lista.
Por que importa
O coringa aplica um comando a muitos arquivos. O for aplica uma sequência de decisões a cada um — inclusive com if dentro.
echo "--- Verificando pontos de montagem ---" for PONTO in / /home /tmp; do if [ -d "$PONTO" ]; then ESPACO=$(df -h "$PONTO" | tail -1 | awk '{print $4}') echo "$PONTO tem $ESPACO livres" else echo "$PONTO não existe nesta máquina" fi done
uma linha por caminho. O teste -d verifica se o diretório existe antes de consultar o df — assim o script não quebra se receber um caminho que não existe nesta máquina. Esse tipo de defesa é o que diferencia script frágil de script confiável. Repare também: se /home não for uma partição separada (o caso da sua VM), o df mostra o sistema de arquivos que o contém — o mesmo de / — e os dois aparecem com o mesmo espaço livre.
Passo 5 de 7
Uma função para registrar log
Adicione a função log() no topo (logo após o shebang)O que é
nome() { ... } define a função. Dentro dela, $1 é o primeiro argumento recebido na chamada.
Por que importa
Se amanhã o formato do log mudar, você altera um lugar só — em vez de caçar todos os echo espalhados pelo script.
#!/bin/bash ARQUIVO_LOG=~/monitor.log log() { echo "[$(date +'%F %T')] $1" | tee -a "$ARQUIVO_LOG" } # ... resto do script ... log "Verificação iniciada"
a mensagem aparece na tela e é gravada no arquivo. O tee -a faz as duas coisas ao mesmo tempo (o -a é de "append", equivalente ao >> do Encontro 12). Confira com cat ~/monitor.log.
Por que a função precisa vir antes de ser chamada?
O bash lê o script de cima para baixo. Se você chamar log numa linha anterior à definição da função, ele ainda não sabe que ela existe e dá "command not found". Por isso funções ficam sempre no topo, logo após o shebang.
qual a vantagem de usar uma função log() em vez de repetir echo em vários pontos do script?
0 / 40 caracteres
Passo 6 de 7
O script completo
Versão final do monitor.shO que é
Todas as peças anteriores integradas, mais o exit com código apropriado ao final.
Por que importa
O exit 1 sinaliza erro a quem chamou o script — é assim que uma ferramenta de agendamento ou um pipeline sabe que algo deu errado.
#!/bin/bash ARQUIVO_LOG=~/monitor.log LIMITE=80 log() { echo "[$(date +'%F %T')] $1" | tee -a "$ARQUIVO_LOG" } log "Verificação iniciada" USO=$(df / | tail -1 | awk '{print $5}' | tr -d '%') if [ "$USO" -gt "$LIMITE" ]; then log "ALERTA: disco em ${USO}%, acima do limite" exit 1 else log "OK: disco em ${USO}%" exit 0 fi
depois de rodar, verifique o código de saída com echo $? — deve ser 0 se estiver OK. Teste também ~/monitor.sh && echo "tudo certo": a segunda parte só executa se o script sair com 0, exatamente como o && que vocês usam desde o Encontro 5.
Desafio: faça o script sair com código 2 e uma mensagem de erro se USO não for um número (dica: [[ "$USO" =~ ^[0-9]+$ ]] testa se a variável só tem dígitos).
Passo 7 de 7
Um script que garante o container no ar
nano ~/garante_container.shO que é
docker ps -q -f name=... devolve o ID do container se ele estiver rodando, ou nada se não estiver. O teste -z verifica se a variável ficou vazia.
Por que importa
É a lógica básica de qualquer ferramenta de orquestração: verificar o estado desejado e agir só quando ele não for o real.
#!/bin/bash NOME=meuservico RODANDO=$(docker ps -q -f name="$NOME") if [ -z "$RODANDO" ]; then echo "Container $NOME não está no ar. Subindo..." docker run -d --name "$NOME" alpine sleep 600 else echo "Container $NOME já está rodando (ID: $RODANDO)" fi
na primeira execução, o script sobe o container. Rode uma segunda vez — agora ele deve detectar que já está no ar e não fazer nada. Essa propriedade (rodar várias vezes sem causar estrago) é justamente o que se espera de um script de automação.
Segunda execução deu erro de nome já em uso
Significa que o container existe mas está parado — o docker ps -q só lista os que estão rodando. Remova com docker rm $NOME e rode de novo. Um script de produção trataria esse caso também, verificando com docker ps -aq; fica como desafio.
Limpeza: docker stop meuservico && docker rm meuservico.
o que aconteceu ao rodar o garante_container.sh duas vezes seguidas? Por que essa propriedade é desejável num script de automação?
0 / 40 caracteres
Exemplo de observação forte × fraca
Observação fraca
"O $(...) executa um comando e guarda o resultado."
Observação forte
"O $(...) executa o comando e substitui a expressão pela saída dele. Na linha USO=$(df / | tail -1 | awk '{print $5}' | tr -d '%'), todo o pipeline roda primeiro e só o número final (ex.: 36) é guardado em USO. Sem isso, o script só conseguiria imprimir a saída do df na tela; com isso, ele consegue comparar esse valor com o limite e decidir se alerta. É o que separa um script que informa de um que monitora."
Checklist final
- Criei meu primeiro script com shebang e
chmod +x - Usei variáveis e capturei a saída de um comando com
$(...) - Fiz o script decidir com
ife testei os dois caminhos - Percorri uma lista com
for, comifdentro - Criei a função
log()e confirmei a gravação no arquivo - Montei o script completo e verifiquei o código de saída com
echo $? - Escrevi um script que verifica e sobe um container, e rodei duas vezes
- Li as explicações deste roteiro por inteiro (é a leitura da semana)
- Preenchi as 3 observações acima (vou colar no quiz semanal)