Uma das minhas diversões nesta Copa foi montar um álbum de figurinhas. Na verdade, montei o álbum porque me filho queria muito, mas me diverti também, eu acho.
Parte importante de colecionar figurinhas é trocar as repetidas. Através de mensagens em grupos de WhatsApp, dizemos quais repetidas temos e quais figurinhas ainda precisamos. Como programador, me recusei a ficar comparando as listas, então escrevi um programinha em Python (com doctests e tudo) para encontrar intersecções.
O laptop sumido
Semana passada vieram à minha casa para trocar figurinhas. Eu tinha as listas de repetidas e necessárias, tanto minhas quanto da outra colecionadora, mas o meu script estava em um outro laptop. Eu nem sabia onde esse laptop estava, e a visita estava com pressa.
Não daria tempo de achar o computador, ou de reescrever o programa. Ou mesmo de comparar manualmente.
Hora de usar alguns comandos Unix!
O formato das listas
As listas geralmente têm esse formato:
15, 18, 26, 31, 40, 45 (2), 49, 51, 110, 115, 128, 131 (2), 143, 151, 161, 162, 183 (2), 216 (2), 221, 223, 253, 267 (3), 269, 280, 287, 296, 313, 325, 329, 333 (2), 353 (3), 355, 357, 359, 362, 365, 366, 371, 373, 384, 399, 400, 421 (2), 445, 457, 469, 470, 498 (2), 526, 536, 553, 560, 568, 570, 585, 591 (2), 604 (2), 639 (2), 660.
Basicamente, eu precisava remover tudo que não fossem dígitos, além dos números entre parênteses, e comparar duas listas. Fácil até.
Preprocessando com sed
Primeiro, preciso remover os contadores entre parênteses:
$ cat list.txt | sed 's/([^)]*)//g'
15, 18, 26, 31, [...] 591 , 604 , 639 , 660.
(Eu sei, UUOC. Que seja.)
Depois, coloco cada número em uma linha:
$ cat list.txt | sed 's/([^)]*)//g' | sed 's/, */\n/g'
Depois, limpo cada linha de qualquer caractere que não seja um dígito:
cat list.txt | sed 's/([^)]*)//g' | sed 's/, */\n/g' | sed 's/[^0-9]*\([0-9]*\)[^0-9]*/\1/g'
(Na prática chamo sed
apenas uma vez, passando duas expressões. Aqui acho que fica mais claro invocar sed
várias vezes.)
Por fim, ordeno os valores:
$ cat list.txt | sed 's/([^)]*)//g' | sed 's/, */\n/g' | sed 's/[^0-9]*\([0-9]*\)[^0-9]*/\1/g' | sort -n > mine-needed.txt
Faço isso com a lista de figuras necessárias, e também com a lista de figuras repetidas, conseguindo dois arquivos.
Encontrando intersecções com grep
Agora, preciso compará-los. Há muitas opções, e eu escolhi usar grep
.
No caso, chamei grep
passando um dos arquivos como entrada, e o outro arquivo como uma lista de padrões para casar, através da opção -f
. Além disso, apenas o matching completo das linhas importa, então usaremos a flag -x
. Por fim, pedi para grep
comparar strings diretamente (ao invés de considerá-las expressões regulares) com a flag -F
.
$ fgrep -Fxf mine-needed.txt theirs-repeated.txt
253
269
333
470
639
Pronto! Em um minuto, já sei quais figuras quero. Basta fazer o mesmo com as minhas repetidas.
Por que isto é interessante?
Para mim, hoje, estes one-liners não são grande coisa. O interessante é que, quando comecei a usar o terminal, eles seriam incríveis. Sério, olhe quantos pipes usamos para preprocessar os arquivos! E esse truque com o grep
? Eu penava só para fazer uma regex que funcionasse! Na verdade, até solucionar esse problema, eu nem conhecia a opção -x
.
Certa vez ajudei um amigo meu a processar um bom número de arquivos. Ele já levava mais de duas horas tentando fazer isso com Java, e resolvemos juntos em dez minutos com shell script. Ele então me falou o quanto queria saber shell script e me perguntou como aprender.
Pois bem, pequenos exemplos como estes, por simples que sejam, me ensinaram muito. É assim que se aprende: tentando resolver problemas, conhecendo comandos e opções aos poucos. E, no final, esta é uma habilidade muito valiosa.
Então, espero que esta pequena brincadeira enriqueça seu dia, também. Certamente enriqueceu o meu – queria ter pensado nela antes de gastar tanto tempo no script Python que escrevi!