Utilitários Unix, Parte 3

O componente mais fácil que você já escreveu

Peter Seebach ( unixcomponents@seebs.plethora.net)
Escritor freelance
Junho de 2001

As ferramentas Unix são muito mais fáceis de desenvolver que você pensa. Neste artigo iremos explorar como construir algumas ferramentas simples.

Conteúdo

Escrever uma ferramenta é, novamente, supreendentemente fácil. Sua interface é só ler linhas de entrada e escrever linhas de saída. Comunicações de rede são tratadas por ferramentas wrapper. Algumas vezes é útil aceitar nomes de arquivos na linha de comando (ou mesmo na entrada padrão), mas não é necessário, existem ferramentas que fazem isto para você.

Então, para finalizar: leia sua entrada, processe, e escreva sua saída. Se possível, escreva a saída logo que puder, não espere até que toda a entrada seja gerada a menos que seja logicamente necessário (o sort precisa ver toda sua saída, o grep e o uniq, não).

Primeiro, decida o que você quer fazer. Então só leia sua entrada e escreva sua saída.

Resultados parciais são melhores que resultados finais

Muitos usuários ficam chocados incialmente ao descobrir que um utilitário do tipo do PKZIP não é padrão em sistemas unix. Existem programas de compressão de dados, e existem programas arquivadores, mas existem poucos programas que comprimem e arquivam.

Por quê?

Por que quando o projeto GNU liberou um programa de compressão, que era substancialmente melhore que o utilitário compress que todo mundo usava, o arquivador não precisou de alterações. O programador do utilitário de compressão não precisava pensar sobre arquivamento, ou sobre como tratar diferentes sistemas de arquivos. O programador do utilitário de arquivamento não precisou pensar sobre algoritmos de compressão.

Agora eu estou usando outra ferramenta de compressão, cahamada de bzip2. Ela dá uma compressão ainda melhor que o gzip. Eu troquei de algoritmos de compressão duas vezes, e ainda estou usando o mesmo programa para arquivamento. O Unix me permitiu atualizar uma "aplicação" (arquivar e comprimir arquivos) atualizando um componente (o utilitário de compressão). De forma semelhante, eu posso trocar meu programa arquivador, e não preciso me preocupar com as características de compressão. Ele usará a mesma compressão que eu uso agora.

Se você pode dividir uma tarefa em duas partes, e existe alguma forma de uma tarefa ser útil sem a outra, desenvolva-as como duas ferramentas, não como uma única ferramenta.

Eu posso utilizar qualquer programa de arquivamento que eu queira (mesmo um programa especializado que eu escreva para meus propósitos) com qualquer programa de compressão que eu queira. Esta é uma funcionalidade que nenhum outro Sistema Operacional conseguiu igualar.

Neste artigo, iremos ver como aplicar estes princípios na criação de novas ferramentas.

Leia sua entrada

Uma ferramenta Unix bem comportada geralmente irá obter sua entrada em uma de duas formas. Ela pode ler do fluxo de dados da entrada padrão -- esta é a forma mais comum para um filtro (uma ferramenta que simplesmente manipula um fluxo de dados). Ou ela pode ler a entrada de arquivos, que são geralmente nomeados na linha de comando.

Uma ferramenta Unix somente "precisa" realmente ler na entrada padrão. Dadas uma lista de arquivos, você pode sempre rodar o cat nas mesmas para transformar as mesmas em um fluxo de dados, próprio para a conexão à entrada padrão de qualquer ferramenta que você queira.

Não há muito mais sobre isto. Na maioria das linguagens, ler uma linha de entrada é totalmente trivial. Em algumas arquiteturas de componentes, você pode precisar de uma página de código para tratar todos os "métodos" de entrada que você vai precisar.

Obviamente, existe uma armadilha aqui. Em C, que é uma das linguagens de desenvolvimento Unix mais comuns, é um pouco complicado ler linhas de comprimento arbitrário. Veremos um exemplo concreto sobre isto mais adiante, neste artigo.

Escrevendo a saída

Uma ferramenta geralmente irá escrever sua saída tão logo ela esteja pronta, assim o próximo programa na pipeline pode começar a trabalhar tão logo seja possível: distribuir a carga de trabalho é tudo.

Escrever a saída é na verdade mais fácil que ler a entrada. Você não tem nem mesmo o problema de alocação para se preocupar.

Exemplos

Alguns exemplos simples de ferramentas irão dar a você um sentimento de como as ferramentas Unix podem ser simples e fáceis. Alguns exemplos um pouco mais especializados são dados a seguir para mostrar como você pode abordar problemas específicos.

Note que em muitas implementações atuais do Unix, como o Linux ou o FreeBSD, o sistema vem com o código fonte completo. Mesmo em outros sistemas, você pode usar o código fonte destas ferramentas (a maioria delas é bastante portável entre sistemas Unix-like) para aprender mais sobre o desenvolvimento de ferramentas. Se exiset uma ferramenta que quase faz o que você quer, provavelmente você consegue os fontes dela e alterar a mesma.

Exemplo: sl

Este é um testemunho silenciosos ao incrível poder do Perl. Este script ordena as linhas pelo comprimento.

	#!/usr/bin/perl5
	print sort { length $a length $b };

Isto é tudo o que você precisa para escrever uma simples ferramenta Unix. A entrada e saída estão tão padronizadas que a suposições lógicas podem ser feitas por padrão, permitindo que programas simples sejam bastante expressivos.

Exemplo: unsort

Certa vez eu estava testando um programa de ordenamento. Obviamente, eu precisava de grandes quantidades de dados não ordenados para a entrada, de forma que eu escrevi um programa para desordenar a entrada para os testes.

O programa é um pouco maior que eu esperava inicialmente que ele foss, pro que eu queria que ele fosse robusto ao tratar de grandes arquivos de entrada. Entretanto, agora que o código está escrito, eu sempre posso reutilizar o emsmo. A estrutura descreve exatamente o que as ferramentas Unxi fazem: elas lêem a entrada, elas a modificam de alguma forma, e elas escrevem a saída. Meu utilitário é exatamente igual aos outros, e eu posso plugar ele em qualquer coisa (por exemplo, meu editor pode agora colocar um bloco de texto fora de ordem).

O unsort é um utilitário particularmente simples. Ele não precisa tratar qualquer opção -- trocar aleatóriamente a ordem das linhas não depende de uma "chave" de forma alguma. O código fonte do usort está em anexo (veja Recursos para um link).

Esta é uma ferramenta completa. Ela pode processar arquivos ou só uma stream de entrada, e é capaz de tratar grandes quantias de dados muito bem. A única falha real é que o gerador de números aleatórios do C padrão é geralmente atroz, especialmente em plataformas Unix (obviamente você pode substituir por outro).

Exemplo: shred

Conforme eu mencionei antes, ocasionalmente é legal poder pegar uma ferramenta que funciona de uma forma, e fazer ela funcionar de outra. Por exemplo, a maior parte dos filtros Unix não funciona "in place" -- eles pegam entrada e produzem saída, e qundo você quer rodar os mesmos em um arquivo, eles escrevem o conteúdo (modificado) do arquivo na saída padrão.

O shell scrip shred pega um filtro como argumento, e roda o mesmo, in place, em um determinado número de arquivos. ele pode receber os arquivso como argumento de linha de comando, ele pode pegar uma lista de arquivos na entrada padrão, ou ele pode gerar uma lista de arquivos que atendem um dado critério, como "arquivos com sufixo .c".

Note que este programa não usa a mesma interface que a maioria das ferramentas Unix. Isto por que é uma das ferramentas que alteram o modelo. Assim como o rsh precisa saber sobre as conexões de rede de forma que outros utilitários não o necessitem, ele também executa uma tarefa de porte ao tentar encontrar os arquivos nos quais você quer operar, e tentando descobrir como tratar os mesmos de forma segura.

O schred é um exemplo particularmente interessante de ferramenta, por que ele não pode fazer nada. Se você não lhe der um comando, tudo que ele pode fazer é imprimir uma mensagem informando como usar, e sair. E ainda, uma vez que você informe um comando, ele pode fazer todo tipo de coisa. A razão que a interface das ferramentas Unix ainda é simples depois de todos estes anos é que você sempre pode acrescentar uma interface às ferramentas existentes.

Imprimindo registros bem formatados

Imagine que você tenha alguns registros em um formato que se parece com o seguinte:

Account: NO_ACCT                        Name: foo
Sales Rep: n/a                          District: blah
City: Unknown                           Start Date: Unknown

Account: 2923912                        Name: Smith
Sales Rep: Anyone But Bob               District: Unknown
City: Unknown                           Start Date: 4/1/01

Como faremos estes registros funcionarem com as ferramentas Unix? Teremos de convertê-los! Os utilitários prettyread e prettyprint trabalham com este tipo de dado. O prettyread assume que a segunda coluna começa exatamente a 40 caracteres. E se os dados são indentados usando tabulações, ao invés de espaços? Por sorte, existe uma ferramenta especial para isto, o expand. Ele troca as tabulações pelo número correspondente de espaços.

Assim, se passarmos o arquivo acima em nosso utilitário prettyread, terminaremos com o seguinte:

NO_ACCT:foo:n/a:blah:Unknown:Unknown
2923912:Smith:Anyone But Bob:Unknown:Unknown:4/1/01

Se passarmos esta informação pelo prettyprint, obteremos os dados originais novamente.

Esta ferramenta nos permite utilizar todas as ferramentas padrão em nossos dados, sem ter de nos preocupar em afetar acidentalmente as etiquetas ao invés dos dados, ou qualquer coisa parecida. Ao invés de escrever um programa que ordene os registros no novo formato, ou modificar o sort padrão para tratar estes registros estranhos, simplesmente modificamos nossos registros para um formato mais útil, trabalhamos com eles nesta forma, e então transformamos os mesmos novamente. Obviamente, o prettyprint precisa de algum trabalho se queremos generalizar o mesmo, mas ele não precisa de muito trabalho -- por que ele faz somente uma coisa.

Além disto, isto nos dá uma funcionalidade adicional. O tratamento dos valores padrão e a independência da ordem dos registros de entrada significa que o prettyread vai funcionar bem se você lhe informar somente registros parciais, ou registros onde os dados estão fora de ordem. por exemplo, dada a seguinte entrada:

City: Saint Paul			Name: Jane Doe
District: Midwest
Start Date: 2/1/00

Obtemos a seguinte saída:

Account: NO_ACCT                        Name: Jane Doe
Sales Rep: Unassigned                   District: Midwest
City: Saint Paul                        Start Date: 2/1/00

Quando estiver escrevendo suas próprias ferramentas, lembre-se sempre: Faça uma coisa, faça-a bem, e se algo mais precise ser feito, faça-o seapradamente.

Em nosso quarto e último artigo desta série, irei dar algumas lições sobre Unix para arquiteturas de componentes.

Recursos

Sobre o autor

Quando criança, Peter Seebach pensou que "rm" era um nome perfeitamente intuitivo para o comando que "ReMove" um arquivo. Desde então tem sido um defensor incondicional do Unix. Você pode entrar em contato com ele em unixcomponents@seebs.plethora.net.

1