Explore as capacidades dos canais do Linux para harmonizar a atuação de
ferramentas de linha de comando. Simplifique operações complexas e
potencialize sua produtividade, utilizando um conjunto de comandos
independentes e transformando-os em um time eficaz. Vamos mostrar como
fazer isso.
A Onipresença dos Pipes
Os pipes são um dos recursos de linha de comando mais valiosos que o Linux e
outros sistemas do tipo Unix oferecem. Sua aplicação é vasta. Ao analisar
qualquer tutorial de linha de comando do Linux, em qualquer plataforma, é
notável a frequência com que os pipes são empregados. Uma análise de
artigos sobre Linux revela que eles estão presentes em praticamente todos,
de uma forma ou de outra.
Os canais do Linux possibilitam a execução de tarefas que não são
diretamente suportadas pelo
Shell
. A essência do design do Linux é ter diversas ferramentas pequenas, cada uma
executando sua
função específica com precisão
e sem funcionalidades extras, seguindo o lema “faça uma coisa e faça bem”.
Assim, podemos usar pipes para conectar comandos, fazendo com que a saída de
um se torne a entrada do seguinte. Cada comando contribui com sua
especialidade, criando uma combinação poderosa.
Um Exemplo Introdutório
Imagine um diretório com diversos arquivos de variados tipos. Nosso objetivo
é descobrir a quantidade de arquivos de um tipo específico nesse diretório.
Embora existam outras formas de conseguir isso, este exercício visa
introduzir o conceito de pipes.
Podemos listar os arquivos facilmente utilizando o comando ls:
ls
Para filtrar os arquivos de nosso interesse, utilizaremos o comando grep.
Nossa meta é localizar arquivos que incluam a palavra “página” em seus
nomes ou extensões.
Utilizaremos o caractere “|” do shell para direcionar a saída de ls para
grep.
ls | grep "page"
O comando grep exibe as linhas que
correspondem ao critério de busca
. Assim, obtemos uma listagem apenas com os arquivos “.page”.
Mesmo esse exemplo simples demonstra a funcionalidade dos pipes. A saída do
comando ls não é exibida na janela do terminal, mas é enviada como entrada
para o comando grep. A saída que vemos é a do comando grep, o último da
cadeia.
Aprimorando a Sequência de Comandos
Vamos expandir nossa cadeia de comandos. Podemos
contar o número de arquivos “.page”
adicionando o comando wc. Usaremos a opção -l (contagem de linhas) com wc.
Note que também adicionamos a opção -l (formato longo) ao comando ls.
Utilizaremos isso em breve.
ls -l | grep "page" | wc -l
O comando grep não é mais o último da cadeia, portanto, não vemos sua
saída. A saída de grep é encaminhada para wc. A saída exibida no terminal é
a de wc, que informa que existem 69 arquivos “.page” no diretório.
Vamos adicionar mais um passo. Retiraremos o comando wc e o substituiremos
por awk. A saída de ls com a opção -l (formato longo) tem nove colunas.
Utilizaremos awk para
exibir as colunas
cinco, três e nove, que correspondem ao tamanho, proprietário e nome do
arquivo, respectivamente.
ls -l | grep "page" | awk '{print $5 " " $3 " " $9}'
Obtemos uma lista com as colunas selecionadas para cada arquivo.
Agora, vamos encaminhar essa saída para o comando sort. Usaremos a opção -n
(numérico) para indicar que a primeira coluna deve ser
tratada como números
.
ls -l | grep "page" | awk '{print $5 " " $3 " " $9}' | sort -n
A saída agora está organizada por tamanho de arquivo, com a seleção de três
colunas.
Adicionando Mais um Comando
Para finalizar, adicionaremos o comando tail. Instruiremos o sistema a
exibir apenas as
últimas cinco linhas da saída
.
ls -l | grep "page" | awk '{print $5 " " $3 " " $9}' | sort -n | tail -5
Com isso, nosso comando passa a significar algo como “liste os cinco
maiores arquivos ‘.page’ neste diretório, ordenados por tamanho”. Não
existe um comando único para isso, mas com o uso de pipes, criamos o nosso.
Essa combinação de comandos, ou qualquer outro comando longo, pode ser
salva como um alias ou função shell para evitar a necessidade de redigitação.
Eis o resultado:
Podemos inverter a ordem do tamanho adicionando a opção -r (reverso) ao
comando sort e utilizando head em vez de tail para exibir as linhas do
início da saída
.
Desta vez, os cinco maiores arquivos “.page” são apresentados do maior para
o menor:
Exemplos Práticos
A seguir, apresentamos dois exemplos práticos recentes de uso de pipes.
Certos comandos, como o xargs, são projetados para
receber dados via pipe
. Este é um método para contabilizar
palavras, caracteres e linhas
em múltiplos arquivos, direcionando a saída de ls para xargs, que repassa a
lista de arquivos para wc como se fossem parâmetros de linha de comando.
ls *.page | xargs wc
A contagem total de palavras, caracteres e linhas é exibida na parte
inferior da janela do terminal.
Este é um modo de obter uma lista organizada das extensões de arquivo
únicas no diretório atual, com uma contagem de cada tipo.
ls | rev | cut -d'.' -f1 | rev | sort | uniq -c
Há muita ação acontecendo aqui.
ls: Lista os arquivos do diretório
rev:
Inverte a ordem dos caracteres
nos nomes dos arquivos.
cut:
Separa a string
no primeiro delimitador “.”, descartando o que vem depois.
rev: Inverte a ordem dos caracteres na string restante, que é a extensão
do arquivo.
sort: Organiza a lista em ordem alfabética.
uniq: Conta o número de cada
entrada única na lista
.
O resultado é a lista de extensões de arquivos, organizadas
alfabeticamente, com a contagem de cada tipo.
Pipes Nomeados
Existe outro tipo de pipe disponível, denominado pipe nomeado. Os pipes
utilizados nos exemplos anteriores são criados pelo shell durante o
processamento da linha de comando. São criados, usados e descartados. São
transitórios, não deixando vestígios, e existem apenas enquanto o comando
que os emprega está em execução.
Os pipes nomeados são representados como objetos permanentes no sistema de
arquivos, visíveis através do comando ls. São persistentes, sobrevivendo
ao reinício do computador, embora os dados não lidos sejam descartados.
Houve um tempo em que os pipes nomeados eram muito utilizados para permitir
a comunicação de dados entre diferentes processos, porém, seu uso atual
não é tão frequente. Apesar disso, existem usuários que ainda se beneficiam
de suas capacidades. Para fins de completude, e para satisfazer a
curiosidade, veremos como eles podem ser usados.
Pipes nomeados são criados com o comando mkfifo. Este comando
criará um pipe nomeado
chamado “geek-pipe” no diretório atual.
mkfifo geek-pipe
Podemos verificar os detalhes do pipe nomeado com o comando ls usando a
opção -l (formato longo):
ls -l geek-pipe
O primeiro caractere da listagem é “p”, indicando que se trata de um pipe.
Um “d” indicaria que o objeto é um diretório, e um “-” que é um arquivo
normal.
Usando o Pipe Nomeado
Vamos utilizar o pipe criado. Os pipes não nomeados usados nos exemplos
anteriores encaminhavam os dados instantaneamente do comando emissor para
o receptor. Os dados enviados por um pipe nomeado permanecem no pipe até
que sejam lidos. Esses dados são armazenados na memória, por isso o tamanho
do pipe nomeado não varia em listagens do comando ls, independentemente da
presença de dados.
Utilizaremos duas janelas de terminal para este exemplo. As identificaremos
como:
# Terminal-1
em uma janela e
# Terminal-2
na outra, para facilitar a distinção. O caractere “#” indica que é um comentário e será ignorado pelo shell.
Vamos pegar o exemplo anterior e redirecionar a saída para o pipe nomeado.
Assim, estaremos utilizando tanto pipes não nomeados quanto pipes nomeados
em um único comando:
ls | rev | cut -d'.' -f1 | rev | sort | uniq -c > geek-pipe
Aparentemente nada acontece. Note que o cursor não retorna ao prompt de
comando, indicando que algo está em andamento.
Na outra janela do terminal, execute o seguinte comando:
cat < geek-pipe
Estamos redirecionando o conteúdo do pipe nomeado para o comando cat, que
exibirá esse conteúdo na segunda janela do terminal. Veja o resultado:
Observe que o prompt de comando foi retomado na primeira janela do terminal.
Então, o que aconteceu?
Redirecionamos a saída para o pipe nomeado.
A primeira janela do terminal não retornou ao prompt de comando.
Os dados permaneceram no pipe até serem lidos no segundo terminal.
Retornamos ao prompt de comando na primeira janela do terminal.
É possível que você pense que poderíamos executar o comando na primeira
janela como uma tarefa em segundo plano, adicionando um & ao final do
comando. E você estaria certo. Nesse caso, o prompt de comando retornaria
imediatamente.
O objetivo de não utilizar o processamento em segundo plano era enfatizar
que um pipe nomeado é um processo de bloqueio. A escrita em um pipe nomeado
abre apenas uma das extremidades do pipe. A outra extremidade só é aberta
quando o programa de leitura extrai os dados. O kernel suspende o processo
na primeira janela do terminal até que os dados sejam lidos da outra
extremidade do pipe.
O Poder dos Pipes
Atualmente, os pipes nomeados são mais uma curiosidade.
Por outro lado, os pipes tradicionais do Linux são uma das ferramentas mais
poderosas disponíveis na linha de comando. A linha de comando ganha vida e
oferece novas possibilidades ao permitir a orquestração de comandos para
alcançar resultados complexos.
Um conselho final: é recomendável criar os comandos canalizados
gradativamente, adicionando um comando por vez e verificando se cada etapa
funciona corretamente antes de adicionar o próximo comando.