Lendo e escrevendo arquivos com o redirecionamento no Shell

Lendo e escrevendo arquivos com o redirecionamento no Shell
Gabriel de Russo e Carmo
Gabriel de Russo e Carmo

Compartilhe

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
 $ 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
Banner da Escola de DevOps: Matricula-se na escola de DevOps. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

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.

Gabriel de Russo e Carmo
Gabriel de Russo e Carmo

Veja outros artigos sobre DevOps