Lendo e escrevendo arquivos com o redirecionamento no Shell

(Última atualização em: 6 de março de 2017)

Sou monitor de um professor na faculdade e uma das minhas tarefas é corrigir alguns trabalhos.

A turma tem uns 40 alunos e eles farão 3 trabalhos ao longo do semestre. No final do curso, preciso fazer um relatório para o professor, indicando quais alunos estão de recuperação, reprovados ou aprovados.

Rapidamente fiz um programa em Python que lê uma lista de alunos e suas notas e imprime na tela a situação de cada um. O código ficou mais ou menos assim:

import sys

for linha in sys.stdin:
    linha = linha.split(';')
    aluno = linha[0]
    trabalho1 = int(linha[1])
    trabalho2 = int(linha[2])
    trabalho3 = int(linha[3])
    media = (trabalho1 + trabalho2 + trabalho3) / 3
    print(aluno + ': ', end='')
    if media >= 5:
        print('Aprovado')
    elif media >= 3:
        print('Recuperação')
    else:
        print('Reprovado')

Não tem problema se você não manja de Python, o importante aqui é entender que esse código lê várias linhas do teclado na forma:

Nome do aluno;Nota1;Nota2;Nota3

E imprime na tela algo como:

Nome do aluno: Aprovado

A execução do código é a seguinte:

$ python media.py
> João da Silva;8;7;9
João da Silva: Aprovado
> Larissa Souza;10;9;10
Larissa Souza: Aprovado
> Mario Oliveira;5;4;5
Mario Oliveira: Recuperação
> Pedro Carvalho;3;0;1
Pedro Carvalho: Reprovado

Embora o programa funcione, existem alguns detalhes que ainda dificultam a rotina de gerar o relatório, vamos entender quais são:

  • O programa imprime imediatamente depois que digito, deixando o resultado final confuso.
  • Dá muito trabalho rodar de novo. Se eu esquecer de um aluno, preciso digitar 40 nomes com 3 notas cada de novo.

Se o programa lesse os alunos e notas de um arquivo, daria para modificar só uma linha facilmente. Uma opção é alterar o código para trabalhar com arquivos. Essa alternativa é viável porque o código é meu e eu sei trabalhar com Python. Ficaria mais ou menos assim:

arquivo = open('notas.txt', 'r') #abrindo arquivo

for linha in arquivo: #lendo do arquivo
    linha = linha.split(';')
    aluno = linha[0]
    ...
 

Entendendo o redirecionamento

Nem sempre temos o luxo de ter acesso ao código fonte dos programas que usamos. Então como podemos fazer para ler as informações de um arquivo e direcionar cada linha para o nosso programa sem mexer no código?

Em sistemas baseados no Unix (como é o caso do Linux e do Mac), todos os programas são associados a uma entrada e saída padrão. Em geral, a entrada é o teclado e a saída é a tela (terminal). Podemos redirecionar tanto a entrada quanto a saída padrão de qualquer programa sem mexer no código, podendo facilmente ler e escrever em arquivos.

Redirecionando a entrada

Para redirecionar a entrada do código original, basta utilizar o caractere < na hora de rodar o programa, indicando um arquivo de entrada. Então vamos criar o arquivo notas.txt com o conteúdo:

João da Silva;8;7;9
Larissa Souza;10;9;10
Mario Oliveira;5;4;5
Pedro Carvalho;3;0;1

Rodando o programa da seguinte forma:

$ python media.py < notas.txt
João da Silva: Aprovado
Larissa Souza: Aprovado
Mario Oliveira: Recuperação
Pedro Carvalho: Reprovado

O resultado já ficou muito melhor. Agora posso copiar e colar a saída e mandar para o professor tranquilamente. Também posso mudar o nome do arquivo a ser lido com facilidade.

Modificando o código para escrever num arquivo

Já melhorou bastante, porém estamos lidando apenas com 4 alunos. E se fossem 1000? Copiar 1000 linhas do terminal não é nada produtivo. Seria uma boa jogar a saída num arquivo também, certo?

Podemos fazer isso modificando o nosso código, da seguinte forma:

import sys

saida = open('resultado.txt', 'w') # abrindo arquivo de saida

for linha in sys.stdin:
    linha = linha.split(';')
    aluno = linha[0]
    trabalho1 = int(linha[1])
    trabalho2 = int(linha[2])
    trabalho3 = int(linha[3])
    media = (trabalho1 + trabalho2 + trabalho3) / 3
    saida.write(aluno + ': ') # escrevendo no arquivo
    if media >= 5:
        saida.write('Aprovado\n') # escrevendo no arquivo
    elif media >= 3:
        saida.write('Recuperação\n') # escrevendo no arquivo
    else:
        saida.write('Reprovado\n') # escrevendo no arquivo

saida.close() # fechando o arquivo de saida

Observe que modificamos algumas boas linhas de código. Meio trabalhoso, não acha?

Redirecionando a saída

Que tal utilizarmos o redirecionamento também? É muito mais cômodo porque não vamos mexer em código.

Para redirecionar a saída do código original, basta utilizar o caractere > na hora de rodar o programa, de forma semelhante ao redirecionamento da entrada:

$ python media.py > resultado.txt
> João da Silva;8;7;9
> Larissa Souza;10;9;10
> Mario Oliveira;5;4;5
> Pedro Carvalho;3;0;1

E se olharmos o conteúdo de resultado.txt, veremos:

João da Silva: Aprovado
Larissa Souza: Aprovado
Mario Oliveira: Recuperação
Pedro Carvalho: Reprovado

Redirecionando entrada e saída ao mesmo tempo

Uma coisa legal sobre o redirecionamento é que podemos fazer uma combinação. Não há nenhum problema em redirecionar tanto a entrada quanto a saída, isto é, podemos realizar o redirecionamento de saída e entrada ao mesmo tempo, veja como:

$ python media.py < notas.txt > resultado.txt

Quando fazemos esse redirecionamento o nosso programa primeiro lê o arquivo notas.txt e depois escreve em resultado.txt.

Truncamento

Agora os principais detalhes foram resolvidos, vou corrigir os trabalhos e gerar o relatório para o professor.

Como a turma é tanto para alunos do curso de Ciência da Computação como para alunos de Sistemas de Informação, decidi colocar as notas em dois arquivos diferentes: notas_cc.txt e notas_si.txt. Seus conteúdos são mais ou menos o seguinte:

notas_cc.txt
Julia Aparecida;8;9;9
Gabriel Russo;2;4;2

notas_si.txt
Danilo Gomes;10;9;10
Horacio Soares;5;7;4

Vou rodar meu programa duas vezes e meu relatório estará pronto:

$ python media.py < notas_cc.txt > resultado.txt
$ python media.py < notas_si.txt > resultado.txt

Olhando o conteúdo de resultado.txt:

Danilo Gomes: Aprovado
Horacio Soares: Aprovado

O que houve com os alunos de Ciência da Computação? Eles deveriam estar no arquivo também.

O problema é que quando redirecionamos a saída de um programa para um arquivo, o sistema operacional trunca o arquivo antes, isto é, apaga todo seu conteúdo. Não é isso que queremos… E agora?

Podemos redirecionar a saída com os caracteres >>, que fazem exatamente a mesma coisa com a diferença de que o arquivo não é truncado. Veja:

$ python media.py < notas_cc.txt > resultado.txt
$ python media.py < notas_si.txt >> resultado.txt

Na primeira vez, truncamos o arquivo e escrevemos. Na segunda, apenas escrevemos. Olhando para o conteúdo de resultado.txt vemos:

Julia Aparecida: Aprovado
Gabriel Russo: Reprovado
Danilo Gomes: Aprovado
Horacio Soares: Aprovado

Para saber mais

Além de ler e escrever em arquivos, com esses redirecionamentos é possível fazer os programas conversarem, ou seja, podemos redirecionar a saída de um programa para a entrada de outro.

O programa ps com a flag -x imprime todos os processos que estão rodando nesse momento. Muitas vezes, esta lista é enorme e fica muito difícil procurar um processo. Podemos utilizar o programa grep para filtrar esses resultados.

O grep funciona de várias formas, mas uma delas é procurar um texto na entrada. Mas como redirecionar a saída do ps para a entrada do grep ?

Poderíamos utilizar um arquivo para guardar a saída e depois utilizá-lo como entrada, porém podemos fazer isso diretamente utilizando o caractere | (chama-se pipe). Basicamente, pegamos a saída padrão de um programa e jogamos para a entrada padrão de outro. Vamos ver um exemplo:

$ ps -x | grep Eclipse
41891 ?? 0:37.48 /Users/gabriel/Applications/Eclipse.app/Contents/MacOS/eclipse

Agora que eu sei que meu processo tem o PID 41891, eu posso rodar kill 41891 para fechar o Eclipse travado.

Resumindo

Nesse post aprendemos que é possível ler e escrever em arquivos sem precisar mexer no código de nossos programas. Para fazer isso, utilizamos os seguintes redirecionamentos:

  • < para redirecionar a entrada
  • > para redirecionar a saída truncando (apagando) o arquivo
  • >> para redirecionar a saída sem truncar o arquivo

Que tal aprender mais sobre o Shell e Linux no geral? Aqui na Alura temos diversos cursos online de Linux para você aprender desde a estrutura básica do Linux como também os principais programas e comandos desse sistema operacional.

  • Prof. Ed – ED1rac

    Excelente post.

Próximo ArtigoPortfólio na área de tecnologia: um guia para construir um ideal!