{
 "grupo": "shell",
 "descricao": "Shell e terminal",
 "itens": [
  {
   "id": "sh-achar-arquivo",
   "nome": "Achar arquivos por nome",
   "receita": "fd nome ou find . -name '*.ext' -not -path '*/node_modules/*'",
   "prova": "lista com caminhos"
  },
  {
   "id": "sh-achar-texto",
   "nome": "Achar texto no código",
   "receita": "rg -n \"texto\" --glob '!node_modules'; -C 2 para contexto",
   "prova": "linhas com arquivo:número"
  },
  {
   "id": "sh-trocar-em-massa",
   "nome": "Trocar texto em vários arquivos",
   "receita": "rg -l \"velho\" ",
   "prova": " xargs sed -i '' 's/velho/novo/g' (macOS); rode o rg antes para ver o alvo"
  },
  {
   "id": "sh-processos",
   "nome": "Ver e matar processo",
   "receita": "lsof -i :PORTA ou ps aux ",
   "prova": " rg nome; kill PID (kill -9 só se travar)"
  },
  {
   "id": "sh-porta-ocupada",
   "nome": "Porta já em uso",
   "receita": "lsof -nP -iTCP:3000 -sTCP:LISTEN; mate o PID ou use outra porta",
   "prova": "servidor sobe na porta"
  },
  {
   "id": "sh-espaco-disco",
   "nome": "O que ocupa disco",
   "receita": "du -sh * ",
   "prova": " sort -h "
  },
  {
   "id": "sh-tamanho-arquivos",
   "nome": "Maiores arquivos do projeto",
   "receita": "find . -type f -not -path './.git/*' -exec du -k {} + ",
   "prova": " sort -n "
  },
  {
   "id": "sh-variaveis",
   "nome": "Variável de ambiente certa",
   "receita": "printenv NOME; export NOME=valor vale só na sessão; arquivo .env com set -a; . ./.env; set +a",
   "prova": "echo \"${NOME:+definida}\""
  },
  {
   "id": "sh-cron",
   "nome": "Agendar tarefa",
   "receita": "crontab -e: min hora dia mes semana comando; no macOS prefira launchd; log com >> arquivo 2>&1",
   "prova": "execução registrada no log"
  },
  {
   "id": "sh-pipe-json",
   "nome": "Ler JSON no terminal",
   "receita": "curl -s url ",
   "prova": " jq '.campo[] "
  },
  {
   "id": "sh-baixar",
   "nome": "Baixar arquivo",
   "receita": "curl -fL -o saida url (falha com código); confira o tamanho",
   "prova": "ls -lh e file saida"
  },
  {
   "id": "sh-comprimir",
   "nome": "Compactar e extrair",
   "receita": "tar -czf a.tgz pasta; tar -xzf a.tgz; unzip -l a.zip lista sem extrair",
   "prova": "listagem confere"
  },
  {
   "id": "sh-permissao",
   "nome": "Permissão negada",
   "receita": "ls -l; chmod u+x script; dono errado: chown (com cuidado, nunca recursivo na raiz)",
   "prova": "comando roda"
  },
  {
   "id": "sh-script-seguro",
   "nome": "Script bash robusto",
   "receita": "set -euo pipefail; aspas em \"$var\"; trap para limpar; shellcheck",
   "prova": "shellcheck sem aviso"
  },
  {
   "id": "sh-rodar-paralelo",
   "nome": "Rodar tarefas em paralelo",
   "receita": "xargs -P 4 -n 1 comando < lista; ou & e wait",
   "prova": "todas terminam e o código de saída é checado"
  },
  {
   "id": "sh-observar-log",
   "nome": "Seguir um log",
   "receita": "tail -f arquivo ",
   "prova": " rg --line-buffered ERRO"
  },
  {
   "id": "sh-medir-tempo",
   "nome": "Medir tempo de comando",
   "receita": "time comando; hyperfine 'a' 'b' para comparar",
   "prova": "tempo anotado antes e depois"
  },
  {
   "id": "sh-diff-pastas",
   "nome": "Comparar duas pastas",
   "receita": "diff -rq a b; rsync -avn a/ b/ simula",
   "prova": "lista de diferenças"
  },
  {
   "id": "sh-editar-linha",
   "nome": "Mudar uma linha de arquivo",
   "receita": "sed -n 'N,Mp' arq para ler; edite com a ferramenta de edição, não com sed frágil",
   "prova": "trecho relido confere"
  },
  {
   "id": "sh-rede-teste",
   "nome": "Testar conexão",
   "receita": "curl -I url; nc -vz host porta; dig dominio",
   "prova": "status e tempo anotados"
  },
  {
   "id": "sh-historico",
   "nome": "Repetir comando anterior",
   "receita": "ctrl-r busca; !! repete; evite colar senha no histórico",
   "prova": "comando repetido sem segredo no histórico"
  },
  {
   "id": "shell-procurar-por-conteudo-ignorando-pastas",
   "nome": "Procurar por conteúdo ignorando pastas",
   "receita": "rg \"texto\" -g '!node_modules' -g '!dist' ou grep -rn --exclude-dir=node_modules",
   "prova": "resultados sem arquivos de dependência"
  },
  {
   "id": "shell-listar-so-arquivos-de-um-tipo",
   "nome": "Listar só arquivos de um tipo",
   "receita": "fd -e ts ou find . -name '*.ts' -not -path '*/node_modules/*'",
   "prova": "lista só os .ts do projeto"
  },
  {
   "id": "shell-contar-linhas-de-codigo",
   "nome": "Contar linhas de código",
   "receita": "find . -name '*.js' -not -path '*/node_modules/*' | xargs wc -l | tail -1",
   "prova": "um total de linhas por tipo"
  },
  {
   "id": "shell-ordenar-e-contar-repetidos",
   "nome": "Ordenar e contar repetidos",
   "receita": "sort | uniq -c | sort -rn | head",
   "prova": "tabela de frequência da mais comum à menos"
  },
  {
   "id": "shell-extrair-uma-coluna",
   "nome": "Extrair uma coluna",
   "receita": "awk '{print $3}' arquivo ou cut -d, -f2 arquivo.csv",
   "prova": "só a coluna pedida na saída"
  },
  {
   "id": "shell-somar-uma-coluna",
   "nome": "Somar uma coluna",
   "receita": "awk '{s+=$2} END {print s}' arquivo",
   "prova": "o total bate com uma soma manual de amostra"
  },
  {
   "id": "shell-filtrar-por-condicao",
   "nome": "Filtrar por condição",
   "receita": "awk -F, '$3 > 100 {print $1}' arquivo.csv",
   "prova": "só linhas que satisfazem a condição"
  },
  {
   "id": "shell-substituir-texto-com-seguranca",
   "nome": "Substituir texto com segurança",
   "receita": "sed -i.bak 's/velho/novo/g' arquivo (no macOS: sed -i '' ou -i.bak)",
   "prova": "git diff mostra só a troca desejada"
  },
  {
   "id": "shell-ver-so-um-trecho-de-arquivo",
   "nome": "Ver só um trecho de arquivo",
   "receita": "sed -n '10,40p' arquivo ou head -n 40 | tail -n 31",
   "prova": "as linhas 10 a 40 na tela"
  },
  {
   "id": "shell-ver-o-fim-de-arquivo-grande",
   "nome": "Ver o fim de arquivo grande",
   "receita": "tail -n 200 arquivo, ou tail -f para seguir",
   "prova": "as últimas 200 linhas"
  },
  {
   "id": "shell-procurar-com-contexto",
   "nome": "Procurar com contexto",
   "receita": "grep -n -C 3 \"erro\" arquivo",
   "prova": "3 linhas antes e depois de cada ocorrência"
  },
  {
   "id": "shell-procurar-palavra-inteira",
   "nome": "Procurar palavra inteira",
   "receita": "grep -w \"id\" arquivo",
   "prova": "não casa com \"idade\" nem \"valid\""
  },
  {
   "id": "shell-procurar-com-regex-estendida",
   "nome": "Procurar com regex estendida",
   "receita": "grep -E \"erro|falha|timeout\" arquivo",
   "prova": "as três variações encontradas"
  },
  {
   "id": "shell-excluir-linhas",
   "nome": "Excluir linhas",
   "receita": "grep -v \"DEBUG\" arquivo",
   "prova": "saída sem as linhas de DEBUG"
  },
  {
   "id": "shell-listar-arquivos-que-contem-texto",
   "nome": "Listar arquivos que contêm texto",
   "receita": "grep -rl \"TODO\" src",
   "prova": "só os nomes dos arquivos"
  },
  {
   "id": "shell-ler-um-json-bonito",
   "nome": "Ler um JSON bonito",
   "receita": "jq . arquivo.json",
   "prova": "JSON indentado"
  },
  {
   "id": "shell-pegar-um-campo-do-json",
   "nome": "Pegar um campo do JSON",
   "receita": "jq -r '.itens[].nome' arquivo.json",
   "prova": "um nome por linha"
  },
  {
   "id": "shell-filtrar-json-por-valor",
   "nome": "Filtrar JSON por valor",
   "receita": "jq '.[] | select(.status==\"ativo\")' arquivo.json",
   "prova": "apenas itens ativos"
  },
  {
   "id": "shell-json-para-csv",
   "nome": "JSON para CSV",
   "receita": "jq -r '.[] | [.id,.nome] | @csv' arquivo.json",
   "prova": "CSV com as colunas escolhidas"
  },
  {
   "id": "shell-ler-yaml",
   "nome": "Ler YAML",
   "receita": "yq '.servicos.web.image' arquivo.yaml",
   "prova": "o valor do campo"
  },
  {
   "id": "shell-chamar-api-com-cabecalho",
   "nome": "Chamar API com cabeçalho",
   "receita": "curl -sS -H \"Authorization: Bearer $TOKEN\" URL | jq .",
   "prova": "resposta formatada e status esperado"
  },
  {
   "id": "shell-enviar-json",
   "nome": "Enviar JSON",
   "receita": "curl -sS -X POST URL -H 'Content-Type: application/json' -d @corpo.json",
   "prova": "status 2xx e o corpo da resposta"
  },
  {
   "id": "shell-ver-so-o-status-e-o-tempo",
   "nome": "Ver só o status e o tempo",
   "receita": "curl -s -o /dev/null -w '%{http_code} %{time_total}s\\n' URL",
   "prova": "200 e o tempo em segundos"
  },
  {
   "id": "shell-seguir-redirecionamentos",
   "nome": "Seguir redirecionamentos",
   "receita": "curl -sIL URL",
   "prova": "a cadeia de redirecionamentos com os status"
  },
  {
   "id": "shell-baixar-com-retomada",
   "nome": "Baixar com retomada",
   "receita": "curl -C - -O URL ou wget -c URL",
   "prova": "o arquivo completo (confira o tamanho ou checksum)"
  },
  {
   "id": "shell-verificar-checksum",
   "nome": "Verificar checksum",
   "receita": "shasum -a 256 arquivo e compare com o publicado",
   "prova": "os hashes são idênticos"
  },
  {
   "id": "shell-gerar-uma-senha-aleatoria",
   "nome": "Gerar uma senha aleatória",
   "receita": "openssl rand -base64 24",
   "prova": "uma string de 32 caracteres"
  },
  {
   "id": "shell-gerar-uuid",
   "nome": "Gerar UUID",
   "receita": "uuidgen ou python3 -c 'import uuid;print(uuid.uuid4())'",
   "prova": "um UUID válido"
  },
  {
   "id": "shell-codificar-e-decodificar-base64",
   "nome": "Codificar e decodificar base64",
   "receita": "echo -n texto | base64 e base64 -d (no macOS use -D se preciso)",
   "prova": "o texto original volta"
  },
  {
   "id": "shell-hash-de-texto",
   "nome": "Hash de texto",
   "receita": "printf %s \"texto\" | shasum -a 256",
   "prova": "hash estável para o mesmo texto"
  },
  {
   "id": "shell-data-em-formato-iso",
   "nome": "Data em formato ISO",
   "receita": "date -u +%Y-%m-%dT%H:%M:%SZ",
   "prova": "data em UTC no formato ISO 8601"
  },
  {
   "id": "shell-converter-timestamp",
   "nome": "Converter timestamp",
   "receita": "date -r 1700000000 no macOS ou date -d @1700000000 no Linux",
   "prova": "data legível correspondente"
  },
  {
   "id": "shell-esperar-um-servidor-subir",
   "nome": "Esperar um servidor subir",
   "receita": "until curl -sf localhost:3000/health; do sleep 1; done",
   "prova": "o laço termina quando o serviço responde"
  },
  {
   "id": "shell-rodar-com-limite-de-tempo",
   "nome": "Rodar com limite de tempo",
   "receita": "timeout 30 comando (macOS: gtimeout do coreutils)",
   "prova": "o comando é interrompido após 30 s"
  },
  {
   "id": "shell-repetir-comando-que-falha-de-vez-em-quando",
   "nome": "Repetir comando que falha de vez em quando",
   "receita": "for i in 1 2 3; do comando && break; sleep $((i*2)); done",
   "prova": "o comando sucede ou falha após 3 tentativas"
  },
  {
   "id": "shell-deixar-processo-rodando-apos-fechar-o-terminal",
   "nome": "Deixar processo rodando após fechar o terminal",
   "receita": "nohup comando > saida.log 2>&1 & e guarde o PID",
   "prova": "o processo continua após fechar o terminal"
  },
  {
   "id": "shell-esperar-processo-em-segundo-plano",
   "nome": "Esperar processo em segundo plano",
   "receita": "PID=$!; wait $PID e confira o código de saída",
   "prova": "o código de saída é capturado"
  },
  {
   "id": "shell-ver-o-codigo-de-saida",
   "nome": "Ver o código de saída",
   "receita": "comando; echo $? (0 é sucesso)",
   "prova": "o número retornado condiz com o resultado"
  },
  {
   "id": "shell-encadear-com-seguranca",
   "nome": "Encadear com segurança",
   "receita": "comando1 && comando2 só continua se o 1 sucede; use o operador OU para tratar a falha",
   "prova": "o segundo comando não roda quando o primeiro falha"
  },
  {
   "id": "shell-redirecionar-saida-e-erro",
   "nome": "Redirecionar saída e erro",
   "receita": "comando > saida.txt 2> erro.txt ou comando &> tudo.txt",
   "prova": "cada arquivo com o conteúdo esperado"
  },
  {
   "id": "shell-descartar-saida",
   "nome": "Descartar saída",
   "receita": "comando > /dev/null 2>&1",
   "prova": "nada no terminal e o código de saída continua"
  },
  {
   "id": "shell-mostrar-e-salvar",
   "nome": "Mostrar e salvar",
   "receita": "comando | tee saida.txt",
   "prova": "o texto aparece e o arquivo é gravado"
  },
  {
   "id": "shell-entrada-de-varias-linhas",
   "nome": "Entrada de várias linhas",
   "receita": "cat <<'EOF' > arquivo ... EOF (aspas evitam expansão)",
   "prova": "o arquivo contém o texto literal"
  },
  {
   "id": "shell-processar-linha-a-linha",
   "nome": "Processar linha a linha",
   "receita": "while IFS= read -r linha; do ...; done < arquivo",
   "prova": "cada linha é processada, inclusive com espaços"
  },
  {
   "id": "shell-arquivos-com-espaco-no-nome",
   "nome": "Arquivos com espaço no nome",
   "receita": "find ... -print0 | xargs -0 comando",
   "prova": "todos os arquivos processados sem quebrar nomes"
  },
  {
   "id": "shell-loop-sobre-arquivos",
   "nome": "Loop sobre arquivos",
   "receita": "for f in *.png; do ...; done com [[ -e \"$f\" ]] OU continue (test -e)",
   "prova": "nenhum erro quando não há arquivos"
  },
  {
   "id": "shell-renomear-extensao-de-varios-arquivos",
   "nome": "Renomear extensão de vários arquivos",
   "receita": "for f in *.jpeg; do mv \"$f\" \"${f%.jpeg}.jpg\"; done",
   "prova": "os arquivos têm a nova extensão"
  },
  {
   "id": "shell-quebrar-caminho-e-nome",
   "nome": "Quebrar caminho e nome",
   "receita": "basename e dirname, ou ${f##*/} e ${f%/*}",
   "prova": "o nome e o diretório isolados"
  },
  {
   "id": "shell-valor-padrao-de-variavel",
   "nome": "Valor padrão de variável",
   "receita": "${VAR:-padrao} usa padrão; ${VAR:?mensagem} aborta se vazio",
   "prova": "a execução falha com a mensagem quando falta variável"
  },
  {
   "id": "shell-ler-argumentos",
   "nome": "Ler argumentos",
   "receita": "getopts ou case \"$1\" in --flag) ...; esac e mensagem de uso",
   "prova": "-h mostra a ajuda e opção inválida sai com código 2"
  },
  {
   "id": "shell-funcoes-reutilizaveis",
   "nome": "Funções reutilizáveis",
   "receita": "defina funções no topo, use local para variáveis, retorne status",
   "prova": "chamar a função duas vezes dá resultados independentes"
  },
  {
   "id": "shell-arquivo-temporario-seguro",
   "nome": "Arquivo temporário seguro",
   "receita": "tmp=$(mktemp) e trap 'rm -f \"$tmp\"' EXIT",
   "prova": "o arquivo some ao sair, mesmo com erro"
  },
  {
   "id": "shell-impedir-execucao-dupla",
   "nome": "Impedir execução dupla",
   "receita": "exec 9>/tmp/x.lock; flock -n 9 OU exit 1 (Linux) ou mkdir como trava",
   "prova": "a segunda instância sai imediatamente"
  },
  {
   "id": "shell-depurar-um-script",
   "nome": "Depurar um script",
   "receita": "bash -x script.sh ou set -x em um trecho; shellcheck antes",
   "prova": "a saída mostra cada comando expandido"
  },
  {
   "id": "shell-testar-scripts",
   "nome": "Testar scripts",
   "receita": "bats-core com casos de entrada e saída esperada",
   "prova": "bats roda e passa nos casos"
  },
  {
   "id": "shell-alias-e-funcoes-do-dia-a-dia",
   "nome": "Alias e funções do dia a dia",
   "receita": "defina em ~/.zshrc, com nome curto e documentação, e recarregue com source",
   "prova": "o alias funciona numa nova aba"
  },
  {
   "id": "shell-historico-util",
   "nome": "Histórico útil",
   "receita": "HISTSIZE grande, HISTCONTROL=ignoredups e Ctrl+R para buscar",
   "prova": "Ctrl+R acha o comando antigo"
  },
  {
   "id": "shell-atalhos-do-terminal",
   "nome": "Atalhos do terminal",
   "receita": "Ctrl+A/E começo e fim, Ctrl+W apaga palavra, Ctrl+U apaga linha",
   "prova": "editar o comando sem mouse"
  },
  {
   "id": "shell-navegar-diretorios",
   "nome": "Navegar diretórios",
   "receita": "cd -, pushd/popd e zoxide ou z para pular",
   "prova": "voltar ao diretório anterior com um comando"
  },
  {
   "id": "shell-arvore-de-diretorios",
   "nome": "Árvore de diretórios",
   "receita": "tree -L 2 -I node_modules ou find . -maxdepth 2 -type d",
   "prova": "estrutura de dois níveis"
  },
  {
   "id": "shell-tamanho-de-cada-pasta",
   "nome": "Tamanho de cada pasta",
   "receita": "du -sh */ | sort -h",
   "prova": "as maiores no final da lista"
  },
  {
   "id": "shell-arquivos-modificados-recentemente",
   "nome": "Arquivos modificados recentemente",
   "receita": "find . -type f -mmin -60 ou -mtime -1",
   "prova": "arquivos mudados na última hora ou dia"
  },
  {
   "id": "shell-apagar-arquivos-antigos",
   "nome": "Apagar arquivos antigos",
   "receita": "find logs -type f -mtime +30 -print e depois -delete",
   "prova": "lista conferida antes da remoção"
  },
  {
   "id": "shell-comparar-arquivos",
   "nome": "Comparar arquivos",
   "receita": "diff -u a b ou diff <(sort a) <(sort b)",
   "prova": "as diferenças linha a linha"
  },
  {
   "id": "shell-mesclar-duas-listas",
   "nome": "Mesclar duas listas",
   "receita": "comm -12 <(sort a) <(sort b) para a interseção",
   "prova": "as linhas comuns"
  },
  {
   "id": "shell-juntar-arquivos-por-coluna",
   "nome": "Juntar arquivos por coluna",
   "receita": "join -t, a.csv b.csv (ambos ordenados pela chave)",
   "prova": "linhas com a chave nos dois"
  },
  {
   "id": "shell-cabecalho-de-csv",
   "nome": "Cabeçalho de CSV",
   "receita": "head -1 arquivo.csv | tr ',' '\\n' | nl",
   "prova": "colunas numeradas"
  },
  {
   "id": "shell-limpar-caracteres",
   "nome": "Limpar caracteres",
   "receita": "tr -d '\\r' < in > out remove CR do Windows; iconv -f latin1 -t utf8",
   "prova": "o arquivo sem ^M e em UTF-8 (file confirma)"
  },
  {
   "id": "shell-converter-codificacao",
   "nome": "Converter codificação",
   "receita": "iconv -f ISO-8859-1 -t UTF-8 entrada > saida",
   "prova": "acentos corretos no resultado"
  },
  {
   "id": "shell-conectar-em-porta",
   "nome": "Conectar em porta",
   "receita": "nc -vz host 5432",
   "prova": "mensagem de conexão bem-sucedida"
  },
  {
   "id": "shell-escutar-uma-porta",
   "nome": "Escutar uma porta",
   "receita": "lsof -i :3000 ou ss -ltnp",
   "prova": "o processo que a ocupa"
  },
  {
   "id": "shell-quem-sou-eu-e-onde",
   "nome": "Quem sou eu e onde",
   "receita": "whoami; hostname; pwd; echo $SHELL",
   "prova": "contexto confirmado antes de comandos arriscados"
  },
  {
   "id": "shell-versoes-das-ferramentas",
   "nome": "Versões das ferramentas",
   "receita": "node -v; python3 --version; git --version em uma linha",
   "prova": "as versões registradas no relato do problema"
  },
  {
   "id": "shell-ver-variaveis-de-ambiente",
   "nome": "Ver variáveis de ambiente",
   "receita": "env | sort | grep -i nome",
   "prova": "o valor atual da variável"
  },
  {
   "id": "shell-carregar-env-no-shell",
   "nome": "Carregar .env no shell",
   "receita": "set -a; source .env; set +a",
   "prova": "as variáveis disponíveis no processo"
  },
  {
   "id": "shell-executar-como-outro-usuario",
   "nome": "Executar como outro usuário",
   "receita": "sudo -u usuario comando",
   "prova": "whoami dentro do comando mostra o usuário"
  },
  {
   "id": "shell-ver-recursos-do-sistema",
   "nome": "Ver recursos do sistema",
   "receita": "top -o cpu (macOS) ou htop; free -h e vm_stat",
   "prova": "o processo mais pesado"
  },
  {
   "id": "shell-numero-de-nucleos",
   "nome": "Número de núcleos",
   "receita": "nproc (Linux) ou sysctl -n hw.ncpu (macOS)",
   "prova": "o número usado para dimensionar paralelismo"
  },
  {
   "id": "shell-medir-uso-de-memoria-de-um-comando",
   "nome": "Medir uso de memória de um comando",
   "receita": "/usr/bin/time -v comando (Linux) ou -l (macOS)",
   "prova": "pico de memória informado"
  },
  {
   "id": "shell-notificar-quando-acabar",
   "nome": "Notificar quando acabar",
   "receita": "comando; osascript -e 'display notification \"pronto\"' ou notify-send",
   "prova": "a notificação aparece ao terminar"
  },
  {
   "id": "shell-compartilhar-pasta-rapidamente",
   "nome": "Compartilhar pasta rapidamente",
   "receita": "python3 -m http.server 8000 na pasta",
   "prova": "curl localhost:8000 lista os arquivos"
  },
  {
   "id": "shell-gerar-arquivo-de-teste",
   "nome": "Gerar arquivo de teste",
   "receita": "dd if=/dev/zero of=teste.bin bs=1m count=10 ou mkfile",
   "prova": "arquivo com o tamanho pedido (ls -l)"
  },
  {
   "id": "shell-pipeline-com-falha-detectada",
   "nome": "Pipeline com falha detectada",
   "receita": "set -o pipefail e confira ${PIPESTATUS[@]}",
   "prova": "falha no meio do pipe derruba o script"
  },
  {
   "id": "shell-sincronizar-pastas",
   "nome": "Sincronizar pastas",
   "receita": "rsync -avh --delete --dry-run origem/ destino/ e depois sem --dry-run",
   "prova": "o dry-run lista o que mudaria e a cópia bate"
  },
  {
   "id": "shell-excluir-em-backup",
   "nome": "Excluir em backup",
   "receita": "rsync --exclude node_modules --exclude .git",
   "prova": "o backup sem as pastas pesadas"
  },
  {
   "id": "shell-auditar-comando-perigoso",
   "nome": "Auditar comando perigoso",
   "receita": "leia o comando, rode com echo ou --dry-run antes de rm, mv, chmod -R, dd",
   "prova": "a lista do que seria afetado confere"
  },
  {
   "id": "shell-cron-com-log",
   "nome": "Cron com log",
   "receita": "*/5 * * * * /caminho/script.sh >> /var/log/x.log 2>&1",
   "prova": "o log mostra execução a cada 5 minutos"
  }
 ]
}
