🌗 ← Índice

🐍 Python P21 — CLI com argparse


Data: 14/08/2026

Nível: Intermediário-Avançado (Mês 3 — Projetos Reais)




🎯 Objetivos





📖 Teoria


Por que argparse


Todo CLI profissional tem: --help, argumentos, flags, validação e mensagens de erro claras. O argparse (biblioteca padrão) entrega tudo isso de graça:



python ferramenta.py --help
# usage: ferramenta.py [-h] [--verbose] arquivo

Primeiro script



import argparse

parser = argparse.ArgumentParser(
    description="Conta palavras de um arquivo de texto",
)

parser.add_argument("arquivo", help="Caminho do arquivo")        # posicional
parser.add_argument("--verbose", action="store_true", help="Mostra detalhes")  # flag
parser.add_argument("-o", "--output", default=None, help="Arquivo de saída")

args = parser.parse_args()
print(f"Arquivo: {args.arquivo}")
print(f"Verbose: {args.verbose}")


python contador.py notas.txt --verbose -o resumo.txt
# Arquivo: notas.txt
# Verbose: True

Tipos de argumento



parser.add_argument("--vezes", type=int, default=1)         # inteiro
parser.add_argument("--taxa", type=float, default=0.1)      # float
parser.add_argument("--itens", nargs="+", default=[])       # lista (1+ itens)
parser.add_argument("--opcao", choices=["rapido", "lento"]) # valores válidos
parser.add_argument("--obrigatorio", required=True)         # obrigatório

Validação



parser.add_argument("--porta", type=int, default=80)

# Validação manual após o parse
if not (1 <= args.porta <= 65535):
    parser.error("A porta deve estar entre 1 e 65535")

Exemplo completo — contador de palavras



import argparse
from pathlib import Path


def contar_palavras(texto: str) -> int:
    return len(texto.split())


def main():
    parser = argparse.ArgumentParser(description="Conta palavras de um arquivo")
    parser.add_argument("arquivo", type=Path, help="Caminho do arquivo")
    parser.add_argument("--saida", "-s", help="Salvar resultado em arquivo")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    if not args.arquivo.exists():
        parser.error(f"Arquivo não encontrado: {args.arquivo}")

    texto = args.arquivo.read_text(encoding="utf-8")
    total = contar_palavras(texto)

    if args.verbose:
        print(f"Arquivo: {args.arquivo} ({len(texto)} caracteres)")
    print(f"Palavras: {total}")

    if args.saida:
        Path(args.saida).write_text(f"{total}\n", encoding="utf-8")
        if args.verbose:
            print(f"Resultado salvo em {args.saida}")


if __name__ == "__main__":
    main()

Subcomandos (nível profissional)



import argparse

parser = argparse.ArgumentParser(description="Ferramenta de backup")
subparsers = parser.add_subparsers(dest="comando", required=True)

# subcomando: backup
parser_backup = subparsers.add_parser("backup", help="Cria um backup")
parser_backup.add_argument("origem", help="Pasta de origem")
parser_backup.add_argument("--destino", default="/tmp/backup")

# subcomando: restaurar
parser_restaurar = subparsers.add_parser("restaurar", help="Restaura um backup")
parser_restaurar.add_argument("arquivo", help="Arquivo .zip")

args = parser.parse_args()

if args.comando == "backup":
    print(f"Backup de {args.origem} → {args.destino}")
elif args.comando == "restaurar":
    print(f"Restaurando {args.arquivo}")


python backup.py backup /etc/nginx
python backup.py restaurar /tmp/backup.zip
python backup.py --help   # lista os subcomandos



✍️ Exercícios


  1. 1. Crie um CLI `saudacao.py` com: argumento posicional `nome` e flag `--formal` (imprime "Bom dia, Sr(a). X" vs "E aí, X!").
  2. 2. Adicione `--idade type=int` com validação (`0 <= idade <= 150`).
  3. 3. Crie um CLI `soma.py` com `nargs="+"` que soma todos os números passados: `python soma.py 1 2 3 4` → `10`.



🧪 Desafio


Crie um CLI organizar_cli.py que envolve o organizador da Aula P19:



python organizar_cli.py /tmp/teste_downloads                # organiza a pasta
python organizar_cli.py /tmp/teste_downloads --tipo pdf     # só PDFs
python organizar_cli.py /tmp/teste_downloads --resumo       # imprime resumo
python organizar_cli.py /tmp/teste_downloads --seco         # simula (não move)

Requisitos: --tipo (filtra por extensão), --resumo (flag), --seco (dry-run, não move nada, só mostra o que faria).




✅ Correção (resumo)



import argparse
from pathlib import Path

TIPOS = {
    "Imagens": {".jpg", ".png", ".gif"},
    "Documentos": {".pdf", ".docx", ".txt", ".md"},
    "Planilhas": {".xlsx", ".csv"},
}


def main():
    parser = argparse.ArgumentParser(description="Organiza arquivos por tipo")
    parser.add_argument("pasta", type=Path, help="Pasta a organizar")
    parser.add_argument("--tipo", default=None, help="Extensão a filtrar (ex: pdf)")
    parser.add_argument("--resumo", action="store_true", help="Imprime resumo")
    parser.add_argument("--seco", action="store_true", help="Modo simulação (não move)")
    args = parser.parse_args()

    if not args.pasta.is_dir():
        parser.error(f"Pasta não encontrada: {args.pasta}")

    movidos = 0
    for arquivo in args.pasta.iterdir():
        if arquivo.is_dir():
            continue
        if args.tipo and arquivo.suffix.lower() != f".{args.tipo.lower()}":
            continue

        categoria = "Outros"
        for nome, ext in TIPOS.items():
            if arquivo.suffix.lower() in ext:
                categoria = nome
                break

        destino = args.pasta / categoria
        if args.seco:
            print(f"[simulado] {arquivo.name} → {categoria}/")
        else:
            destino.mkdir(exist_ok=True)
            arquivo.rename(destino / arquivo.name)
            print(f"Movido: {arquivo.name} → {categoria}/")
        movidos += 1

    if args.resumo or args.seco:
        print(f"\nTotal: {movidos} arquivo(s)")


if __name__ == "__main__":
    main()

💡 O --seco (dry-run) é a prática mais valiosa: permite testar com segurança antes de mover arquivos reais.





HB Estudos · ← Voltar ao índice de Python