Textutils: Utilitários de texto GNU para seu script shell
Como diz o ditado, quem programa scripts shell e não conhece os utilitários básicos de texto está condenado a eternamente reimplementá-los, pobremente.
Comandos como cat, tac, cut, fmt, nl, sort, tr, head e muitos outros primos deles são essenciais. Manipular arquivos de texto, saídas de utilitários e outras fontes de dados em formato textual é o núcleo da lógica de boa parte dos scripts shell que encontro, e dominar os utilitários essenciais que facilitam este processo é parte essencial da trilha que leva ao aprendizado dos shell scripts.
Neste sentido, este artigo do Linux.com traz um apanhado geral sobre alguns dos comandos mais básicos do que antigamente era conhecido como ‘GNU textutils’ (hoje integrado ao coreutils), e recomendo a leitura.
Vale lembrar, entretanto, que a sintaxe das implementações GNU para estes comandos, embora fácil de encontrar em máquinas em que esteja rodando o Linux, frequentemente excede o padrão POSIX para estes utilitários, fazendo com que o uso de suas características extras possa tornar o seu script menos compatível com ambientes UNIX não-GNU ou semi-GNU, ou mesmo em implementações baseadas no Linux com Busybox, por exemplo.
Aham, um bom exemplo disso é o bash (seguido do zsh, que faz tudo que o bash faz e mais), que na versão 4 implementou o suporte à arrays associativos, ou hash, algo que facilita em muitos pontos, mas que não é compatível com o sh padrão.
@tenchi
hauahuahuahau Não teria dito melhor :-D
@tenchi
O problema é que o sh padrão nada mais que um simples symbolic link que indica qual é o interpretador default do sistema, neste caso para o sh, poderia ser o zsh, php, python, entre outros.
Se bem que seria ótimo se fosse adotado o zsh ao invés do bash como shell padrão.
@ Heaven
Não, não é. O SH é emulado pelo Bash e pelo KSH. No caso do Linux o Bash é a shell padrão. Mas, no caso do Unix (os System V), o padrão ainda é o SH, cujas funções “extendidas” são fornecidas pelo KSH e as vezes pelo CSH, sendo que normalmente o primeiro é o principal dos que emulam suas funcionalidades. Bash nesses sistemas, normalmente, não existe.
(No caso do Unix essa “padronização” é ditada pelo Open Group)
Quanto ao BASH, não conheço nada melhor (se o zsh é eu não sei), assim quando pego um *NIX qq, que não o tenha, trato de instalá-lo.
O programa é um SL, está disponível para inúmeras plataformas, então deve ser disseminado. Se faz mais que o Bourne Shell, blz, alguma evolução é normal e necessária.
Só esclarecendo, o que eu escrevi no post foi sobre os texutils, e não sobre o bash, que o Tenchi trouxe pro assunto.
Mas o raciocínio é análogo: quem usa os recursos a mais que o bash (ou outra shell qualquer) oferece, assume uma dependência, e aí se for usar o script em outra máquina, ou oferecer o script para alguém mais usar, esta dependência precisará ser satisfeita.
Às vezes é importante ou necessário fazer isso, e a opção ocorre conscientemente – e tudo vai bem. Por outro lado, para ficar num exemplo simples, às vezes o autor coloca um “ls ––sort=time” no seu script sem nem mesmo saber que se colocasse um “ls -t” (e comentasse adequadamente, caso tivesse a saudável preocupação com a clareza) esta sintaxe menos explícita funcionaria ao longo de todas as implementações compatíveis com o POSIX.
Aí quando chega a hora de oferecer ou compartilhar o script para alguém mais usar, ele falha ou exige manutenção adaptativa, possivelmente sem que nada seja ganho em troca deste inconveniente. E se o próprio autor resolve levar o script para outros ambientes que ele esperava serem compatíveis, pode acabar tendo a mesma surpresa, e aí ver-se obrigado a satisfazer uma dependência que não seria necessária, ou promover a manutenção adaptativa, antes de poder usar o seu script.
Abrir a mão da portabilidade conscientemente, em prol de alguma vantagem mais desejada nas circunstâncias, pode ser normal e positivo. Mas fazê-lo por desconhecer que existe um padrão não é tão bom assim, e por isso coloquei o lembrete.
@ Augusto
Nem precisa ir tão longe (ls –sort=time). O próprio parâmetro -h do ls/df/du e etc, por exemplo, é um ótimo parâmetro. Mas optei por parar de utilizá-lo por que, ao rodar meus scripts em outros SOs (como o NetBSD), os mesmos não funcionavam por coisas assim.
Eu vejo como uma tarefa difícil em fazer um script completamente portável sem ficar demasiadamente capado, mas é possível sim. Os “GNU/Comandos” facilitam muito a vida mesmo…
É por aí, Timm – quando tomamos consciência de que há uma implementação mais portável do que a outra, e ambas produzem a mesma saída, me parece que o critério de escolha fica claro. E quando só uma das duas oferece com comodidade o recurso ou saída desejados, o critério também fica claro…
Eu tenho a vantagem (ou o problema, dependendo do enfoque) de ter aprendido a fazer scripts inicialmente em um ambiente POSIX (embora já trabalhasse, na época, em ambientes com as extensões do GNU). Digo mais: ao longo dos sucessivos treinamentos, o professor nos estimulava a usar uma verificação (‘strictly posix’ ou algo assim) que teoricamente rejeitava a execução quando tentávamos fazer algo disponível no ambiente mas sem conformidade com o padrão.
Com o tempo fui recorrendo cada vez mais às extensões GNU, mas pelo menos faço isso conscientemente, e muito mais nos aspectos em que a implementação GNU oferece algo que a definição POSIX não tem.
Nos aspectos em que a implementação GNU oferece uma sintaxe alternativa, mas o mesmo recurso está disponível (sob outra sintaxe) no POSIX, eu prefiro o POSIX, ao menos quando lembro.
Se necessário, recorro a esta referência do Open Group (especialmente o capítulo Utilities), que pode não ser a mais direta ou completa, mas me quebra o galho ;-)
@Augusto, anotado :-)
Ah sim, eu comentei no início sobre o bash mas somente como um exemplo de reimplementações de softwares pela GNU que estendem os padrões, e isso pode ser bom ou ruim, dependendo de como se olhe. Um outro exemplo é o gawk.
Com relação aos componentes do textutils, vejo como é extremamente comum a pessoa dar, antes de tudo, um cat puro num arquivo, o passando para outro comando, quando o outro comando pode obter o arquivo diretamente da stdin :-)
Já vi (e não vou mentir, já fiz) a seguinte construção:
$ cat arquivo | tac
Pra ler “ao contrário” :-)
Também, quando com sono, já fiz um while lendo linha a linha, incrementando um contador e exibindo na tela, só pra exibir as linhas numeradas. Nem lembrava do nl
Se não me engano o útil operador <<< não existe no sh padrão e pode substituir vários usos do pipe, como por exemplo:
$ rev <<< “Socorram me, subi no ônibus em marrocos”
Ao invés de
echo “Socorram me, subi no ônibus em marrocos” | rev
Tomando que o bash e zsh são superconjuntos do sh e são portáveis para quase todos os unixes do mercado, acho que vale a pena, quando possível, optar por eles ao invés do sh cru.
Infelizmente problemas de licenças impedem que tenhamos uma implementação única do grep, sed e companhia, o que acarreta nestes problemas entre “mudar ou não”.
Alguém sabe se existe alguma movimentação para a criação de um HTML5 pro sh e demais padrões dentro do POSIX? Como em relação à ER e coisas do tipo?
ops, “html5″, entre aspas.
@ Augusto
O link é muito bom, obrigado!
Tenchi, acho que vale a pena só quando há algo a ganhar com a opção. Ter uma versão de shell como dependência só pra poder usar a sintaxe alternativa dela, e não uma das funcionalidades adicionais, não me parece a decisão melhor para a portabilidade de um projeto qualquer.
De fato dá pra portar bash, zsh e boa parte dos coreutils com facilidade pra quase todo canto em que há posix, mas nem sempre é trivial. Você já passou por isso? Eu já, e a sequência foi longa, envolvendo instalar gcc & seus companheiros, downloads diversos de outras dependências, compilação e assim por diante. Nem sempre a coisa se resume a um comando do gerenciador de pacotes para buscar a ferramenta em um bem suprido repositório ;-)