Lendo e escrevendo arquivos com o redirecionamento no Shell
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
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.