Uma das razões pelas quais Python se tornou tão popular é sua capacidade de automatizar tarefas repetitivas do dia a dia — organizar arquivos, renomear em lote, monitorar pastas, manipular o sistema operacional, executar comandos no terminal. O que levaria horas de trabalho manual pode ser resolvido em dezenas de linhas de Python. Neste artigo veremos as principais ferramentas para isso.
os e os.path: Interface com o Sistema Operacional
import os
# Informações do sistema
print(os.name) # 'posix' (Linux/Mac) ou 'nt' (Windows)
print(os.getcwd()) # diretório atual
print(os.getenv("HOME")) # variável de ambiente
# Navegação
os.chdir("/tmp") # muda o diretório atual
# Listagem
for item in os.listdir("."):
print(item)
# Criação e remoção
os.mkdir("nova_pasta")
os.makedirs("a/b/c", exist_ok=True) # cria toda a árvore
os.rmdir("pasta_vazia")
# Renomeando e movendo
os.rename("antigo.txt", "novo.txt")
# Informações de arquivo
stat = os.stat("arquivo.txt")
print(stat.st_size) # tamanho em bytes
print(stat.st_mtime) # timestamp de modificação
# Executando comandos do sistema
codigo = os.system("ls -la") # retorna código de saída
pathlib: A Forma Moderna
Já vimos pathlib no Artigo 19 — aqui exploramos recursos mais avançados:
from pathlib import Path
from datetime import datetime
import shutil
pasta = Path("/home/usuario/documentos")
# Criando estrutura de pastas
(pasta / "projetos" / "python" / "2024").mkdir(parents=True, exist_ok=True)
# Informações detalhadas
arquivo = Path("relatorio.pdf")
if arquivo.exists():
stat = arquivo.stat()
tamanho_mb = stat.st_size / 1024 / 1024
modificado = datetime.fromtimestamp(stat.st_mtime)
print(f"Tamanho: {tamanho_mb:.2f} MB")
print(f"Modificado: {modificado.strftime('%d/%m/%Y %H:%M')}")
# Glob — busca por padrão
pasta_atual = Path(".")
# Todos os .py recursivamente
for py in pasta_atual.rglob("*.py"):
print(py)
# Apenas no nível atual
for txt in pasta_atual.glob("*.txt"):
print(txt)
# Filtrando por tamanho
grandes = [
f for f in pasta_atual.rglob("*")
if f.is_file() and f.stat().st_size > 1024 * 1024 # > 1MB
]
# Leitura e escrita diretas
Path("config.txt").write_text("chave=valor\n", encoding="utf-8")
conteudo = Path("config.txt").read_text(encoding="utf-8")
shutil: Operações de Alto Nível
import shutil
from pathlib import Path
# Copiar arquivo
shutil.copy("origem.txt", "destino.txt") # copia conteúdo
shutil.copy2("origem.txt", "destino.txt") # copia + metadados
# Copiar pasta inteira
shutil.copytree("pasta_origem", "pasta_destino")
# Mover
shutil.move("arquivo.txt", "/outro/caminho/")
# Remover pasta com todo o conteúdo
shutil.rmtree("pasta_para_deletar")
# Compactar e descompactar
shutil.make_archive("backup_2024", "zip", "pasta_fonte")
shutil.make_archive("backup_2024", "gztar", "pasta_fonte") # .tar.gz
shutil.unpack_archive("backup_2024.zip", "destino")
# Espaço em disco
total, usado, livre = shutil.disk_usage("/")
print(f"Total: {total // (2**30)} GB")
print(f"Usado: {usado // (2**30)} GB")
print(f"Livre: {livre // (2**30)} GB")
# Encontrar executável no PATH
python_path = shutil.which("python3")
print(python_path) # /usr/bin/python3
Organizador Automático de Arquivos
from pathlib import Path
from datetime import datetime
import shutil
REGRAS = {
"Imagens": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp"],
"Documentos": [".pdf", ".doc", ".docx", ".txt", ".md", ".odt"],
"Planilhas": [".xls", ".xlsx", ".csv", ".ods"],
"Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv"],
"Audios": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
"Codigo": [".py", ".js", ".ts", ".html", ".css", ".json", ".yml"],
"Compactados":[".zip", ".tar", ".gz", ".rar", ".7z"],
}
def organizar_pasta(caminho: str, simular: bool = True):
"""
Organiza arquivos de uma pasta em subpastas por tipo.
simular=True apenas mostra o que faria, sem mover nada.
"""
pasta = Path(caminho)
movidos = []
ignorados = []
for arquivo in pasta.iterdir():
if not arquivo.is_file():
continue
extensao = arquivo.suffix.lower()
categoria = None
for cat, exts in REGRAS.items():
if extensao in exts:
categoria = cat
break
if not categoria:
categoria = "Outros"
destino_pasta = pasta / categoria
destino_arquivo = destino_pasta / arquivo.name
# Evita sobrescrever arquivos com mesmo nome
if destino_arquivo.exists():
stem = arquivo.stem
sufx = arquivo.suffix
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
destino_arquivo = destino_pasta / f"{stem}_{ts}{sufx}"
movidos.append({
"arquivo": arquivo.name,
"de": str(pasta),
"para": str(destino_pasta),
"categoria": categoria
})
if not simular:
destino_pasta.mkdir(exist_ok=True)
shutil.move(str(arquivo), str(destino_arquivo))
# Relatório
print(f"\n{'=== SIMULAÇÃO ===' if simular else '=== EXECUÇÃO ==='}")
print(f"Pasta: {caminho}")
print(f"Arquivos a mover: {len(movidos)}\n")
from collections import Counter
contagem = Counter(m["categoria"] for m in movidos)
for cat, qtd in sorted(contagem.items()):
print(f" {cat:15}: {qtd} arquivo(s)")
if not simular:
print("\nArquivos movidos com sucesso.")
return movidos
# Primeiro simula, depois executa
movimentos = organizar_pasta("/home/usuario/Downloads", simular=True)
# organizar_pasta("/home/usuario/Downloads", simular=False)
subprocess: Executando Comandos Externos
import subprocess
# Executar e capturar saída
resultado = subprocess.run(
["ls", "-la"],
capture_output=True,
text=True
)
print(resultado.stdout)
print(resultado.returncode) # 0 = sucesso
# Com shell=True — permite pipes e redirecionamentos
resultado = subprocess.run(
"ps aux | grep python | wc -l",
shell=True,
capture_output=True,
text=True
)
print(f"Processos Python: {resultado.stdout.strip()}")
# Verificando erros
try:
subprocess.run(
["git", "status"],
capture_output=True,
text=True,
check=True # lança CalledProcessError se returncode != 0
)
except subprocess.CalledProcessError as e:
print(f"Erro: {e.stderr}")
# Execução com timeout
try:
resultado = subprocess.run(
["ping", "-c", "3", "google.com"],
capture_output=True,
text=True,
timeout=10
)
print(resultado.stdout)
except subprocess.TimeoutExpired:
print("Comando demorou demais.")
# Processo em background
processo = subprocess.Popen(
["uvicorn", "main:app", "--port", "8000"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print(f"PID do servidor: {processo.pid}")
# processo.terminate() # encerra quando necessário
Monitoramento de Pastas com watchdog
pip install watchdog
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from pathlib import Path
from datetime import datetime
import time
import shutil
class ProcessadorArquivos(FileSystemEventHandler):
"""Processa automaticamente arquivos novos em uma pasta."""
def __init__(self, pasta_entrada: str, pasta_processado: str):
self.pasta_entrada = Path(pasta_entrada)
self.pasta_processado = Path(pasta_processado)
self.pasta_processado.mkdir(exist_ok=True)
def on_created(self, event):
if event.is_directory:
return
arquivo = Path(event.src_path)
print(f"[{datetime.now().strftime('%H:%M:%S')}] Novo arquivo: {arquivo.name}")
self._processar(arquivo)
def on_modified(self, event):
if event.is_directory:
return
arquivo = Path(event.src_path)
print(f"[{datetime.now().strftime('%H:%M:%S')}] Modificado: {arquivo.name}")
def on_deleted(self, event):
print(f"[{datetime.now().strftime('%H:%M:%S')}] Deletado: {event.src_path}")
def _processar(self, arquivo: Path):
"""Processa o arquivo conforme sua extensão."""
time.sleep(0.5) # aguarda o arquivo ser completamente escrito
if not arquivo.exists():
return
if arquivo.suffix.lower() == ".csv":
self._processar_csv(arquivo)
elif arquivo.suffix.lower() in [".jpg", ".png"]:
self._processar_imagem(arquivo)
else:
self._mover_para_processados(arquivo, "outros")
def _processar_csv(self, arquivo: Path):
import csv
try:
with open(arquivo, encoding="utf-8") as f:
reader = csv.DictReader(f)
linhas = list(reader)
print(f" CSV processado: {len(linhas)} registros")
self._mover_para_processados(arquivo, "csv")
except Exception as e:
print(f" Erro ao processar CSV: {e}")
def _processar_imagem(self, arquivo: Path):
print(f" Imagem recebida: {arquivo.stat().st_size / 1024:.1f} KB")
self._mover_para_processados(arquivo, "imagens")
def _mover_para_processados(self, arquivo: Path, subcategoria: str):
destino = self.pasta_processado / subcategoria
destino.mkdir(exist_ok=True)
shutil.move(str(arquivo), str(destino / arquivo.name))
print(f" Movido para: {destino}")
def monitorar(pasta_entrada: str, pasta_saida: str):
handler = ProcessadorArquivos(pasta_entrada, pasta_saida)
observer = Observer()
observer.schedule(handler, pasta_entrada, recursive=False)
observer.start()
print(f"Monitorando: {pasta_entrada}")
print("Pressione Ctrl+C para parar.\n")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
# monitorar("/home/usuario/entrada", "/home/usuario/processado")
Scripts com Argumentos: argparse
import argparse
from pathlib import Path
def criar_parser():
parser = argparse.ArgumentParser(
description="Organizador automático de arquivos",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Exemplos:
python organizar.py ~/Downloads
python organizar.py ~/Downloads --executar
python organizar.py ~/Downloads --executar --verbose
python organizar.py ~/Downloads --extensoes .pdf .doc
"""
)
parser.add_argument(
"pasta",
type=str,
help="Caminho da pasta a organizar"
)
parser.add_argument(
"--executar", "-e",
action="store_true",
default=False,
help="Executa as mudanças (padrão: apenas simula)"
)
parser.add_argument(
"--verbose", "-v",
action="store_true",
help="Exibe detalhes de cada arquivo"
)
parser.add_argument(
"--extensoes",
nargs="+",
metavar="EXT",
help="Filtra por extensões específicas (ex: .pdf .jpg)"
)
parser.add_argument(
"--destino", "-d",
type=str,
default=None,
help="Pasta de destino (padrão: subpastas dentro da origem)"
)
return parser
def main():
parser = criar_parser()
args = parser.parse_args()
pasta = Path(args.pasta)
if not pasta.exists():
parser.error(f"Pasta não encontrada: {args.pasta}")
print(f"Pasta: {pasta}")
print(f"Modo: {'EXECUÇÃO' if args.executar else 'SIMULAÇÃO'}")
if args.extensoes:
print(f"Filtro: {', '.join(args.extensoes)}")
movimentos = organizar_pasta(str(pasta), simular=not args.executar)
if args.verbose:
for m in movimentos:
print(f" {m['arquivo']:30} → {m['categoria']}")
if __name__ == "__main__":
main()
# Uso
python organizar.py ~/Downloads
python organizar.py ~/Downloads --executar --verbose
python organizar.py ~/Downloads --extensoes .pdf .doc --destino ~/Documentos
Exemplo Completo: Script de Backup Automatizado
#!/usr/bin/env python3
"""
backup.py — Script de backup com compressão e rotação
Uso:
python backup.py --origem ~/projetos --destino ~/backups
python backup.py --origem ~/projetos --destino ~/backups --manter 7
"""
import argparse
import shutil
import os
from pathlib import Path
from datetime import datetime
def criar_backup(origem: Path, destino: Path, manter: int = 7) -> Path:
"""Cria backup comprimido e remove backups antigos."""
destino.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
nome_base = f"backup_{origem.name}_{timestamp}"
caminho = destino / nome_base
print(f"[{datetime.now().strftime('%H:%M:%S')}] Criando backup...")
print(f" Origem: {origem}")
print(f" Destino: {caminho}.zip")
shutil.make_archive(str(caminho), "zip", str(origem.parent), str(origem.name))
arquivo_zip = Path(f"{caminho}.zip")
tamanho = arquivo_zip.stat().st_size / 1024 / 1024
print(f" Tamanho: {tamanho:.2f} MB")
print(f" Status: OK ✓")
# Rotação — remove backups mais antigos
backups = sorted(destino.glob(f"backup_{origem.name}_*.zip"))
if len(backups) > manter:
for antigo in backups[:-manter]:
antigo.unlink()
print(f" Removido (rotação): {antigo.name}")
return arquivo_zip
def main():
parser = argparse.ArgumentParser(description="Script de backup automatizado")
parser.add_argument("--origem", required=True, help="Pasta a fazer backup")
parser.add_argument("--destino", required=True, help="Onde salvar os backups")
parser.add_argument("--manter", type=int, default=7, help="Backups a manter (padrão: 7)")
args = parser.parse_args()
origem = Path(args.origem).expanduser().resolve()
destino = Path(args.destino).expanduser().resolve()
if not origem.exists():
print(f"Erro: origem não encontrada: {origem}")
exit(1)
arquivo = criar_backup(origem, destino, args.manter)
# Log de auditoria
log = destino / "backup.log"
with open(log, "a", encoding="utf-8") as f:
f.write(f"{datetime.now().isoformat()} | {origem} | {arquivo} | OK\n")
print(f"\nBackup concluído: {arquivo}")
if __name__ == "__main__":
main()
# Executar manualmente
python backup.py --origem ~/projetos --destino ~/backups --manter 5
# Agendar com cron (Linux/Mac)
# crontab -e
# 0 2 * * * /usr/bin/python3 /home/usuario/backup.py --origem ~/projetos --destino ~/backups
Resumo
osoferece interface de baixo nível com o sistema operacional — variáveis de ambiente, processos, permissõespathlib.Pathé a forma moderna e multiplataforma de trabalhar com caminhos e arquivosshutilrealiza operações de alto nível — copiar, mover, compactar e verificar espaço em discosubprocess.run()executa comandos externos com controle de saída, erros e timeoutwatchdogmonitora o sistema de arquivos em tempo real — ideal para pipelines de processamento automáticoargparsetransforma scripts em ferramentas de linha de comando com documentação integrada- Scripts de automação devem ter modo de simulação antes de executar mudanças irreversíveis
- Sempre use
expanduser()eresolve()para normalizar caminhos fornecidos pelo usuário
Referências e Leituras Complementares
- pathlib — documentação oficial — https://docs.python.org/3/library/pathlib.html
- shutil — documentação oficial — https://docs.python.org/3/library/shutil.html
- subprocess — documentação oficial — https://docs.python.org/3/library/subprocess.html
- argparse — documentação oficial — https://docs.python.org/3/library/argparse.html
- watchdog — monitoramento de arquivos — https://watchdog.readthedocs.io/en/latest/
- SWEIGART, Al. Automate the Boring Stuff with Python. 2. ed. No Starch Press, 2019. Cap. 8–10 — automação de arquivos, pastas e processos com exemplos práticos.
- SWEIGART, Al. Beyond the Basic Stuff with Python. No Starch Press, 2020. Cap. 16 — scripts de linha de comando profissionais com Python.