\
named pipe, que também é chamado de FIFO. FIFO é um acrônimo de First In First Out que se refere à propriedade em que a ordem dos bytes entrando no pipe é a mesma que a da saída. O name em named pipe é, na verdade, o nome de um arquivo. Os arquivos tipo named pipes são exibidos pelo comando ls como qualquer outro, com poucas diferenças, veja:
p na coluna mais à esquerda indica que fifo1 é um named pipe. O resto dos bits de controle de permissões, quem pode ler ou gravar o pipe, funcionam como um arquivo normal. Nos sistemas mais modernos uma barra vertical (|) colocado ao fim do nome do arquivo, é outra dica, e nos sistemas LINUX, onde a opção de cor está habilitada, o nome do arquivo é escrito em vermelho por default.
Nos sistemas mais antigos, os named pipes são criados pelo programa mknod, normalmente situado no diretório /etc.
Nos sistemas mais modernos, a mesma tarefa é feita pelo mkfifo. O programa mkfifo recebe um ou mais nomes como argumento e cria pipes com estes nomes. Por exemplo, para criar um named pipe com o nome pipe1, faça:
named pipe mostrado anteriormente. Vamos agora trabalhar com duas sessões ou duas consoles virtuais ou uma de cada. Em uma delas faça:
ls era o "falador" e o cat era o "orelhão".
Uma aplicação muito útil dos named pipes é permitir que programas sem nenhuma relação possam se comunicar entre si, os named pipes também são usados para sincronizar processos, já que em um determinado ponto você pode colocar um processo para "ouvir" ou para "falar" em um determinado named pipe e ele daí só sairá, se outro processo "falar" ou "ouvir" aquele pipe.
Você já viu que o uso desta ferramenta é ótimo para sincronizar processos e para fazer bloqueio em arquivos de forma a evitar perda/corrupção de informações devido a atualizações simultâneas (concorrência). Vejamos exemplos para ilustrar estes casos.
BLOCO1 do Programa1 as três classificações são disparadas da seguinte maneira:
for Arq in BigFile1 BigFile2 BigFile3
do
if sort $Arq
then
Manda=va
else
Manda=pare
break
fi
done
echo $Manda > pipe1
[ $Manda = pare ] &&
{
echo Erro durante a classificação dos arquivos
exit 1
}
...
Assim sendo, o comando if testa cada classificação que está sendo efetuada. Caso ocorra qualquer problema, as classificações seguintes serão abortadas, uma mensagem contendo a cadeia pare é enviada pelo pipe1 e programa1 é descontinuado com um fim anormal.
Enquanto o Programa1 executava o seu primeiro bloco (as classificações) o Programa2 executava o seu BLOCO1, processando as suas rotinas de abertura e menu paralelamente ao Programa1, ganhando desta forma um bom intervalo de tempo.
O fragmento de código do Programa2 a seguir, mostra a transição do seu BLOCO1 para o BLOCO2:
OK=`cat pipe1`
if [ $OK = va ]
then
...
Rotina de impressão
...
else # Recebeu "pare" em OK
exit 1
fi
Após a execução de seu primeiro bloco, o Programa2 passará a "ouvir" o pipe1, ficando parado até que as classificações do Programa1 terminem, testando a seguir a mensagem passada pelo pipe1 para decidir se os arquivos estão íntegros para serem impressos, ou se o programa deverá ser descontinuado. Desta forma é possível disparar programas de forma assíncrona e sincronizá-los quando necessário, ganhando bastante tempo de processamento.
Hits="$(cat page.hits 2> /dev/null)" || Hits=0
echo $((Hits=Hits++)) > page.hits
Desta forma se a página receber dois ou mais acessos concorrentes, um ou mais poderá(ão) ser perdido(s), basta que o segundo acesso seja feito após a leitura da arquivo page.hits e antes da sua gravação, isto é, basta que o segundo acesso seja feito após o primeiro ter executado a primeira linha do script e antes de executar a segunda.
Então o que fazer? Para resolver o problema de concorrência vamos utilizar um named pipe. Criamos o seguinte script que será o daemon que receberá todos os pedidos para incrementar o contador. Note que ele vai ser usado por qualquer página no nosso site que precise de um contador.
echo "teste_pagina.html" > /tmp/pipe_contador
Para evitar erros, em cada página que quisermos adicionar o contador acrescentamos a seguinte linha:
<!--#exec cmd="echo $REQUEST_URI > /tmp/pipe_contador"-->
Note que a variável $REQUEST_URI contém o nome do arquivo que o navegador (browser) requisitou.
Este último exemplo, é fruto de uma idéia que troquei com o amigo e mestre em Shell, Thobias Salazar Trevisan que escreveu o script e colocou-o em sua excelente URL. Aconselho a todos que querem aprender Shell a dar uma olhada nela (Dê uma olhada e inclua-a nos favoritos).
Ahhh! Você pensa que o assunto sobre named pipes está esgotado? Enganou-se. Vou mostrar um uso diferente a partir de agora.
named pipes, agora vou mostrar que o Shell também usa os named pipes de uma maneira bastante singular, que é a substituição de processos (process substitution). Uma substituição de processos ocorre quando você põe um comando ou um pipeline de comandos entre parênteses e um < ou um > grudado na frente do parêntese da esquerda. Por exemplo, teclando-se o comando:
ls -l executado em um subshell como é normal (por estar entre parênteses), porém redirecionará a saída para um named pipe temporário, que o Shell cria, nomeia e depois remove. Então o cat terá um nome de arquivo válido para ler (que será este named pipe e cujo dispositivo lógico associado é /dev/fd/63), e teremos a mesma saída que a gerada pela listagem do ls -l, porém dando um ou mais passos que o usual, isto é, mais onerosa para o computador.
Como poderemos constatar isso? Fácil... Veja o comando a seguir:
named pipe.
Você deve estar pensando que isto é uma maluquice de nerd, né? Então suponha que você tenha 2 diretórios: dir e dir.bkp e deseja saber se os dois estão iguais (aquela velha dúvida: será que meu backup está atualizado?). Basta comparar os dados dos arquivos dos diretórios com o comando cmp, fazendo:
while read arq
do
((i++)) # assim nao eh necessario inicializar i
echo "$i: $arq"
done < <(ls)
echo "No diretorio corrente (`pwd`) existem $i arquivos"
Tá legal, eu sei que existem outras formas de executar a mesma tarefa. Usando o comando while, a forma mais comum de resolver esse problema seria:
ls | while read arq
do
((i++)) # assim nao eh necessario inicializar i
echo "$i: $arq"
done
echo "No diretorio corrente (`pwd`) existem $i arquivos"
Quando executasse o script, pareceria estar tudo certo, porém no comando echo após o done, você verá que o valor de $i foi perdido. Isso deve-se ao fato desta variável estar sendo incrementada em um subshell criado pelo pipe (|) e que terminou no comando done, levando com ele todas as variáveis criadas no seu interior e as alterações feitas em todas as variáveis, inclusive as criadas externamente.
Somente para te mostrar que uma variável criada fora do subshell e alterada em seu interior perde as alterações feitas ao seu final, execute o script a seguir:
#!/bin/bash
LIST="" # Criada no shell principal
ls | while read FILE # Inicio do subshell
do
LIST="$FILE $LIST" # Alterada dentro do subshell
done # Fim do subshell
echo :$LIST:
Ao final da execução você verá que aperecerão apenas dois dois-pontos (::). Mas no início deste exemplo eu disse que era meramente didático porque existem formas melhores de fazer a mesma tarefa. Veja só estas duas:
arq1 e arq2 usando o comando comm, mas este comando necessita que os arquivos estejam classificados. Então a melhor forma de proceder é:
. Curti muito aqui e recebi diversos elogios pelo trabalho desenvolvido ao longo de doze meses e, o melhor de tudo, fiz muitas amizades e tomei muitos chopes de graça com os leitores que encontrei pelos congressos e palestras que ando fazendo pelo nosso querido Brasil.
O que vou escrever aqui não está combinado nem sei se será publicado, mas como os editores desta revista são dois malucos beleza (ambos Rafael), é bem capaz de deixarem passar. É o seguinte: se quiserem que o Papo de Botequim continue, entulhem a caixa postal da Linux Magazine pedindo por isso e desde já escolham o próximo tema entre sed + expressões regulares ou linguagem awk.
De qualquer forma, caso não consigamos sensibilizar a direção da revista, me despeço de todos mandando um grande abraço aos barbudos e beijos às meninas e agradecendo os mais de 100 e-mails que recebi (todos elogiosos) e todos devidamente respondidos.
À saúde de todos nós: Tim, Tim.
- Chico, fecha a minha conta porque vou mudar de botequim.
Vou aproveitar também para mandar o meu jabá: diga para os amigos que quem estiver afim de fazer um curso porreta de programação em Shell que mande um e-mail para a nossa gerencia de treinamento para informar-se.
Qualquer dúvida ou falta de companhia para um chope ou até para falar mal dos políticos é só mandar um e-mail para mim.
Valeu!
(CC) 2009 Pelos Frequentadores do Bar do Júlio Neves.