Utilitários Unix, Parte 1

Mais uma nova arquitetura de componentes

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

Às vezes acontece que quanto mais as coisas mudam, mas elas não mudam. Peter Seebach nos introduz a uma fascinante arquitetura de componentes que nos leva de volta às bases.

Conteúdo


Imagine uma arquitetura de componetes que possui centenas de componentes largamente utilizados e padronizados. Estes componentes possuem uma interface simples e padronizada. Um novo componente não precisa "anunciar" sua interface. A interface padrão é simples e universal. Os componentes não precisam incluir qualquer código para tratar conexões de rede ou outras atividades de interface. Existe um componente "wrapper" separado que trata as tarefas distribuídas por você.

Os programas neste sistema podem geralmente, ou mesmo tipicamente, ser escritos com uma única linha de código. Usuários inexperientes podem ser ensinados a escrever programas simples em alguns minutos. Usuários iniciantes geralmente produzem programas simples sem esforço no seu primeiro dia de uso desta arquitetura.

Este sistema existe, e por mais de 20 anos. É chamado Unix.

Os elementos no ambiente Unix tem sido chamados de ferramentas de software. Uma ferramenta de software é muito parecida com um componente, mas é um pouco diferente. Neste artigo, vou me referir aos componentes como "ferramentas", mas lembre-se que você não pode substituir sempre "componente" por "ferramenta". No Unix, os utilitários padrão do shell são todos ferramentas. O mesmo para o próprio shell. Uma ferramenta usa um formato de dados padrão -- linhas de entrada em formato texto -- e produz saída no mesmo formato. Não é preciso código especial para tratar tipos de dados: todo mundo concorda com um formato simples e executa alguma tradução. Para a maior parte dos programas, não há nenhuma tradução envolvida.

Em geral, uma ferramenta lê um fluxo de linhas de entrada -- que é chamado de entrada padrão -- e escreve um fluxo de linhas de saída -- que é chamado de saída padrão. (Algumas ferramentas não consomem sua entrada uma linha de cada vez, e algumas nem mesmo tratam os fluxos de dados como texto, mas elas geralmente funcionam em fluxos de entrada e saída).

Os usuários Unix aprendem, geralmente nos seus primeiros minutos com o sistema, a combinar ferramentas. Por exemplo, um usuário pode aprender rapidamente a usar

$ more file

para paginar um arquivo. Logo, o usuário está usando o comando more como uma ferramenta:

$ ls ¦ more

Isto não parece programação para nós -- estamos acostumados a que programas são grandes e difíceis de escrever. Mas sim, isto é programação: O usuário combinou duas ferramentas para produzir um programa que executa uma tarefa exatamente conforme as especificações. Não é esta uma das razões para as arquiteturas de componentes em primeiro lugar -- programação ad-hoc e reutilização de código?

O ambiente Unix permite que os usuários combinem peças para construir programas funcionais com uma facilidade que não tem rival em outros istemas. Nesta série de artigos, vou mostrar como o ambiente Unix captura os elementos chave de uma boa arquitetura de compontentes, como construir ferramentas para o mesmo, e como usar estas ferramentas.

O Unix como uma arquitetura de componentes

Realmente, quando se chega à sua essência, a arquitetura de componentes é toda sobre reutilização de código. Como um corolário, esperamos ver programas escritos facilmente e rapidamente. Programas simples devem ser simples de escrever, eles não devem exigir uma quantia enorme de tempo a ser usada copiando modelos e preenchendo os mesmos.

O Unix atinge este objetivo (melhor, eu acho, que qualquer outro sistema em uso generalizado) atravez de um foco na simplicidade. Colocar ferramentas juntas é simplesmente trivial. Um porgrama pode ser uma simples linha, e o ambiente interativo em que muitos programas são escritos é suficientemente expressivo para ser uma interface útil ao sistema. Muitos sistemas fornecem algum tipo de programabilidade limitada, mas não mais do que eles querem que você tenha. O Unix nunca sonhou em limitar a programabilidade de uma ferramenta, e o shell Unix é uma linguagem de programação poderosa e flexível.

Por causa disto, programas simples não precisam ser "construídos". Um usuário que deseja ver uma lista ordenada de registros que combinem com um dado padrão não precisam procurar interfaces para fazer isto; as ferramentas se combinam sem esforço. Você pode precisar verificar como se usa a ferramenta -- como com o cron, que é notório pela sua falta de intuitividade. Mas você nunca precisa fazer a pergunta "como eu forneço a entrada para isto?" por que todos usam stdin.

Fácil de Implementar

O mais interessante é que o desenvolvimento de uma nova ferramenta exige bem pouco esforço. Diferente de sistemas que especificam APIs poderosas (e complicadas) para novas ferramentas, o Unix fornece uma interface simples e genérica; processe a entrada, e produza a saída. Você não precisa ter um mecanismo separado para diferentes tipos de dados, e formas de identificar os mesmos -- você só vai ler a entrada até encontrar uma nova linha, processar a entrada, e escrever a saída terminando com uma nova linha, e repetir. Isto significa que o tempo que você iria gastar tratando da E/S em uma API mais complicada está disponível para fazer algumas atividades reais.

Em alguns casos, obviamente, esta flexibilidade não é o suficiente, e você precisa de um formato mais estruturado.. Ainda assim, o fator de conveniência do desenvolvedor e usuário é maximizado pela interface limitada. Na prática, voê pode executr qualquer tarefa que queira.

Obviamente, esta interface não é completamente genérica. Às vezes você vai querer processar coisas que não se parecem exatamente com um arquivo texto com uma linha por registro. Neste ponto, os projetistas do Unix tomaram uma decisão crucial, que acabou sendo uma decisão correta: Ao invés de exigir que todos os programas desenvolvam uma forma de tratar outros formatos, eles forneceram ferramentas que transformam um formato em outro. A maior parte dos utilitários aceita uma lista de arquivso na linha de comando. Por exemplo, digamos que você queira enviar uma lista de arquivos na entrada padrão. Há uma ferramenta (chamada xargs) que pega uma lista de arquivos na entrada padrão, passa ela pela mesma ferramenta, e deixa que aquela ferramenta produza qualquer saída que quiser.

De forma semelhante, quando as pessoas perceberam que poderia ser útil poder distribuir tarefas pela rede entre múltiplas máquinas, ao invés de forçar todas as ferramenas a aprender sobre interfaces de redes, eles forneceram uma ferramenta "wrapper", que conecta com a máquina remota e executa algo lá. O software na outra ponta não precisa saber (ou se preocupar) se está rodando remotamente ou localmente, ele só pega linhas de entrada e produz linhas de saída.

Esta simplificação significa que é bastante simples escrever uma nova ferramenta para resolver um problema especializado. E, de fato, os usuários Unix tendem a acumular conjuntos de ferramentas personalizadas. Utilitários únicos podem ser baratos o suficienet para ser uma solução razoável para um problema, e você não precisa justificar o tempo de projeto de algo que pode ser escrito em 30 segundos.

Novamente, retirando todas as funcionalidades de facilidade de uso, estamos aptos a produzir um sistema que é realmente fácil de usar. A reutilização de código não significa só projetos medidos em pessoas anos; parte disto é que você pode ter um programa que faz o que você quer hoje -- ou mesmo neste minuto.

Por quê uniq(1) não pode sort(1)

Esta arquitetura também quer dizer que uma ferramenta não precisa ser completa para ser útil. Por exemplo, existe uma ferramenta Unix chamada uniq, que executa tarefas simples envolvendo a eliminação ou contagem de linhas duplicadas. Isto é tudo que ela faz. Ela não detecta linhas duplicadas a menos que elas estejam adjacentes. Se você quer uma lista de ocorrências únicas de um arquivo não ordenado, você precisa ordenar ele primeiro.

Em muitas arquiteturas, a resposta natural seria montar o uniq de forma que ele possa ordenar a entrada. Ele então deve ser capaz de tratar vários tipos de registros que não são linhas, ordenar a entrada, e manter tabelas de hash. Ele ficaria grande e complicado. Acontece que manter as interfaces limpas e separar os componentes produz melhores resultados.

No Unix, mesmo escrevendo em C (que não é a linguagem que usa menos comandos para tratar strings), o uniq tem apenas 4k de código, sem incluir o copyright. Isto inclui todo tratamento de argumentos, incluindo funcionalidades como "ignore os primeiros N campos em cada linha".

Uma vez que a arquitetura torna fácil para o usuário a combinação do uniq com o sort quando necessário, não há necessidade para todas as ferramentas saberem como ordenar. Na verdade, poucos programas realmente fazem o ordenamento. Os que o fazem, o fazem por que os dadas que eles devem usar para colocar os registros em ordem podem não ser visíveis (por exemplo, o ls pode ordenar por que ele pode ordenar arquivos pela data e hora, mesmo que não as apresente).

Finalmente, para um exemplo atraente, vamos ver a forma que os programas Unix que permitem que o usuário faça edições geralmente resolvem seus problemas. O usuário pode configurar uma variável de ambiente -- $EDITOR -- que representa o editor de sua preferência. Quase todos os programas padrão usam esta variável quando precisam fazer alguma edição. Quando o administrador de um sistema BSD Unix quer editar o arquivo de senhas, ele executa um utilitário chamado de vipw. Tudo que este programa faz é:

Você pode ficar tentado a ignorar isto (afinal, é uma tarefa administrativa). Entretanto, o Unix não se preocupa se a tarefa é administrativa; qualquer tarefa que pede um editor vai permitir que você escolha o editor. O Unix não faz você pensar de uma forma para administrar, outra para usar, e outra para programar; programar é usar, e a administração é exatamente como qualquer outra tarefa.

Note que o vipw não precisa ser alterado se você obter um novo editor. De forma similar, seu editor não precisa saber como bloquear um arquivo password. Cada programa faz uma coisa na qual ele é bom, e faz esta coisa bem feita. Seu editor é somente o componente de edição do sistema.

Muitos sistemas alegam permitir que você substitua uma nova ferramenta de edição, uma funcionalidade que o Unix já tem funcionando durante muito tempo (mais adiante na série, iremos ver alguns benefícios surpreendentes que ele tem).

O ambiente Unix -- pela simplificação de sua interface, usando a mesma interface para tudo que for possível, e eliminando distinções artificiais, como uma distinção entre "aplicações" e "componentes" -- fornece todos os benefícios chave de uma arquitetura de componentes supostamente oferece. Em muitas formas, no uso diário, ele ultrapassa qualquer coisa que exista sobre facilidade de reutilização de código.

Na Parte 2, iremos discutir como usar as ferramentas Unix.

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