Scripts de linha de comando frequentemente precisam processar uma sequência de arquivos como se todos formassem um único fluxo. Também é comum aceitar dados pela entrada padrão quando nenhum caminho é informado. O módulo fileinput no Python resolve esses dois casos com uma interface simples: ele percorre os arquivos listados em sys.argv, usa sys.stdin quando a lista está vazia e fornece informações como nome atual, número cumulativo da linha e posição dentro de cada arquivo.
Neste guia, você aprenderá a usar fileinput.input(), a classe FileInput, hooks para arquivos comprimidos, edição in-place com backup, encoding explícito e tratamento seguro de erros. O conteúdo complementa nossos artigos sobre pathlib, tempfile, shlex, filecmp e linecache.
Quando usar fileinput
O módulo é útil quando um programa deve receber zero, um ou vários arquivos e aplicar a mesma lógica linha por linha. Um filtro de texto, normalizador de logs, extrator de dados ou ferramenta de migração pode usar uma única iteração sem abrir cada caminho manualmente.
import fileinput
for linha in fileinput.input(encoding="utf-8"):
processar(linha)Por padrão, os nomes vêm de sys.argv[1:]. Se não houver nenhum, o módulo lê sys.stdin. Assim, o mesmo script pode funcionar com arquivos ou pipes.
Exemplo no terminal
python analisar.py app.log api.log
cat app.log | python analisar.pyNo primeiro comando, as linhas vêm dos dois arquivos. No segundo, a entrada vem do pipe. Um nome igual a - também representa stdin.
Fornecer arquivos explicitamente
Você não precisa depender dos argumentos do processo.
arquivos = ["janeiro.csv", "fevereiro.csv"]
with fileinput.input(files=arquivos, encoding="utf-8") as entrada:
for linha in entrada:
processar(linha)O contexto garante que o arquivo atual e a sequência sejam fechados mesmo se ocorrer uma exceção.
Uma string também é aceita
O parâmetro files pode receber um único nome ou uma sequência.
for linha in fileinput.input(files="dados.txt", encoding="utf-8"):
print(linha, end="")Use uma lista ou tupla quando a coleção for montada dinamicamente.
Quebras de linha são preservadas
As linhas mantêm o caractere de nova linha quando ele existe. A última linha de um arquivo pode não ter terminador.
for linha in fileinput.input(files="dados.txt", encoding="utf-8"):
print(repr(linha))Não remova espaços indiscriminadamente com strip() se eles fizerem parte dos dados. Para remover somente a quebra final, use rstrip("\r\n") com consciência de que isso remove todos os terminadores correspondentes.
Descobrir o arquivo atual
fileinput.filename() devolve o nome da fonte que produziu a linha mais recente.
for linha in fileinput.input(files=arquivos, encoding="utf-8"):
print(fileinput.filename(), linha, end="")Antes de ler a primeira linha, o retorno é None. Funções globais como essa dependem de uma instância ativa criada por fileinput.input().
Número cumulativo da linha
lineno() conta todas as linhas lidas desde o início da sequência.
for linha in fileinput.input(files=arquivos, encoding="utf-8"):
print(fileinput.lineno(), linha, end="")Se o primeiro arquivo tem 100 linhas, a primeira linha do segundo terá número cumulativo 101.
Número dentro do arquivo
filelineno() reinicia a contagem em cada arquivo.
for linha in fileinput.input(files=arquivos, encoding="utf-8"):
print(
fileinput.filename(),
fileinput.filelineno(),
linha,
end="",
)Isso é útil para mensagens de erro que precisam apontar o caminho e a linha local.
Identificar a primeira linha
isfirstline() indica se a linha atual é a primeira do arquivo.
for linha in fileinput.input(files=arquivos, encoding="utf-8"):
if fileinput.isfirstline():
print(f"--- {fileinput.filename()} ---")
print(linha, end="")O método ajuda a tratar cabeçalhos ou imprimir separadores.
Identificar stdin
isstdin() informa se a última linha veio da entrada padrão.
if fileinput.isstdin():
origem = "stdin"
else:
origem = fileinput.filename()Se stdin aparece mais de uma vez na lista, usos posteriores normalmente não produzem novas linhas, porque o fluxo já foi consumido.
Pular o restante do arquivo
nextfile() fecha o arquivo atual e avança para o próximo. Linhas ignoradas não entram na contagem cumulativa.
for linha in fileinput.input(files=arquivos, encoding="utf-8"):
if linha.startswith("FIM"):
fileinput.nextfile()
continue
processar(linha)A função não pode pular o primeiro arquivo antes da primeira linha ser lida. O nome atual só muda quando uma linha do próximo arquivo é obtida.
Usar a classe FileInput
A classe expõe as mesmas operações sem depender das funções globais.
from fileinput import FileInput
with FileInput(files=arquivos, encoding="utf-8") as entrada:
for linha in entrada:
print(
entrada.filename(),
entrada.filelineno(),
linha,
end="",
)Essa forma é melhor quando uma aplicação precisa de várias sequências independentes ou deseja passar o objeto para outra função.
Evitar estado global concorrente
fileinput.input() cria um estado global utilizado por funções como filename(). Uma segunda chamada enquanto a primeira está ativa gera conflito. Em bibliotecas, servidores e código concorrente, prefira instâncias explícitas de FileInput.
Encoding e erros
Desde versões modernas do Python, encoding e errors podem ser passados diretamente.
with fileinput.input(
files=arquivos,
encoding="utf-8",
errors="strict",
) as entrada:
for linha in entrada:
processar(linha)Use strict quando dados inválidos devem interromper o processo. Estratégias como replace ou surrogateescape precisam ser escolhidas conscientemente, porque podem alterar ou preservar bytes problemáticos de formas diferentes.
Modo binário
FileInput aceita mode="r" ou mode="rb".
with fileinput.FileInput(files=arquivos, mode="rb") as entrada:
for linha in entrada:
processar_bytes(linha)No modo binário, as linhas são objetos bytes. Não forneça encoding nessa situação.
Erros de I/O
Ao contrário de linecache, fileinput propaga OSError quando não consegue abrir ou ler uma fonte.
try:
with fileinput.input(files=arquivos, encoding="utf-8") as entrada:
for linha in entrada:
processar(linha)
except OSError as erro:
print(f"falha de leitura: {erro}")Em um lote, decida se a falha de um arquivo deve interromper tudo ou ser registrada para continuação. Fileinput não oferece uma política automática de “ignorar erros”.
Arquivos vazios
Um arquivo vazio é aberto e fechado sem produzir linhas. Sua presença pode passar despercebida. Se arquivos vazios são inválidos, valide o tamanho ou mantenha um registro dos caminhos processados.
Abrir gzip e bzip2
hook_compressed() abre automaticamente arquivos .gz e .bz2.
with fileinput.FileInput(
files=["app.log", "app.log.gz", "antigo.log.bz2"],
openhook=fileinput.hook_compressed,
encoding="utf-8",
) as entrada:
for linha in entrada:
processar(linha)Arquivos com outras extensões são abertos normalmente. A detecção usa o sufixo, não a assinatura real do conteúdo.
Hooks personalizados
Um openhook recebe nome e modo e deve devolver um objeto de arquivo compatível. Quando encoding e errors são informados, o hook também os recebe como keywords.
def abrir_validado(filename, mode, *, encoding=None, errors=None):
caminho = Path(filename).resolve()
if caminho.suffix not in {".txt", ".log"}:
raise ValueError("extensão não permitida")
return open(caminho, mode, encoding=encoding, errors=errors)
with fileinput.FileInput(
files=arquivos,
openhook=abrir_validado,
encoding="utf-8",
) as entrada:
for linha in entrada:
processar(linha)Não use openhook junto com edição in-place.
Edição in-place
Com inplace=True, cada arquivo é movido para um backup e sys.stdout passa a apontar para o arquivo original. Tudo que o script imprime substitui o conteúdo.
for linha in fileinput.input(
files=["config.txt"],
inplace=True,
backup=".bak",
encoding="utf-8",
):
print(linha.replace("antigo", "novo"), end="")Ao terminar, config.txt contém o texto transformado e config.txt.bak preserva o original.
Riscos da edição in-place
A operação é poderosa e pode destruir dados quando o programa imprime menos conteúdo, falha no meio ou escolhe o arquivo errado. Teste primeiro sem inplace, mantenha backup, processe cópias e verifique espaço em disco.
A documentação oficial de fileinput informa que um backup existente com o mesmo nome pode ser substituído silenciosamente. Use uma estratégia própria de nomes quando isso for inaceitável.
Backup padrão
Quando inplace=True e nenhum sufixo permanente é definido, um backup temporário é usado e removido após o fechamento. Para mudanças importantes, especifique uma extensão e preserve o arquivo.
Stdout durante a reescrita
Mensagens de progresso impressas normalmente também seriam gravadas no arquivo. Envie diagnósticos para stderr.
import sys
print("processando", fileinput.filename(), file=sys.stderr)Separe claramente a saída transformada das mensagens operacionais.
In-place não funciona com stdin
A edição no lugar é desativada para entrada padrão porque não existe um arquivo original que possa ser substituído. Um filtro que recebe pipe deve escrever sua saída normalmente e deixar o chamador decidir o destino.
Transações mais seguras
Para dados importantes, uma estratégia com arquivo temporário é mais controlável: escreva em outro caminho, faça flush(), sincronize se necessário, valide o resultado e substitua o original com os.replace().
Essa abordagem permite manter permissões, lidar com erros e criar backups versionados explicitamente.
Não misturar iteração e acesso aleatório
FileInput é uma sequência estritamente linear. Não misture iteração e readline() de formas imprevisíveis, nem espere acesso por índice. Para acesso aleatório por número de linha, use linecache ou leia o arquivo em uma estrutura apropriada.
Segurança com argumentos
Se os nomes vêm de usuários, valide uma raiz permitida e resolva symlinks.
BASE = Path("/srv/importacoes").resolve()
def caminho_permitido(nome):
caminho = (BASE / nome).resolve()
if caminho != BASE and BASE not in caminho.parents:
raise ValueError("caminho fora da área permitida")
return caminhoTambém limite tamanho, quantidade de arquivos e formatos comprimidos para evitar consumo excessivo de recursos.
Arquivos comprimidos hostis
Um arquivo pequeno pode expandir para muitos gigabytes. Aplique limites de bytes, tempo e linhas em entradas desconhecidas. hook_compressed() facilita a abertura, mas não implementa proteção contra bombas de compressão.
Testes
def test_multiplos_arquivos(tmp_path):
a = tmp_path / "a.txt"
b = tmp_path / "b.txt"
a.write_text("um\ndois\n", encoding="utf-8")
b.write_text("três\n", encoding="utf-8")
with fileinput.FileInput(files=[a, b], encoding="utf-8") as entrada:
linhas = list(entrada)
assert linhas == ["um\n", "dois\n", "três\n"]Teste arquivos vazios, última linha sem quebra, encoding inválido, stdin, arquivos comprimidos e falhas durante reescrita.
fileinput versus open()
Use open() para um único arquivo e controle explícito. Use fileinput quando a principal abstração é uma sequência contínua de várias fontes, especialmente em ferramentas de terminal.
Erros frequentes
- Depender acidentalmente de
sys.argv. - Esquecer de declarar encoding.
- Abrir duas sequências globais ao mesmo tempo.
- Usar
strip()e remover espaços significativos. - Imprimir logs em stdout durante edição in-place.
- Reescrever arquivos sem backup permanente.
- Presumir que stdin pode ser lido várias vezes.
- Aceitar arquivos comprimidos sem limites.
Boas práticas
- Use contexto
with. - Prefira
FileInputexplícito em bibliotecas. - Declare encoding e política de erros.
- Envie logs para stderr.
- Preserve backups em operações destrutivas.
- Valide caminhos e tamanhos externos.
- Teste última linha e arquivos vazios.
- Use substituição atômica para dados críticos.
Conclusão
O módulo fileinput no Python simplifica filtros que recebem vários arquivos ou stdin, mantendo informações úteis de origem e numeração. Ele também oferece hooks para compressão e uma edição in-place conveniente.
A simplicidade exige disciplina: evite estado global em código complexo, defina encoding, trate OSError e considere qualquer reescrita como operação destrutiva. Com backups, validação e testes, fileinput é uma excelente base para utilitários de linha de comando claros e compactos.





