You are here:
Wiki-SL
>
TWikiBar Web
>
TWikiBarPapo011
(01 Aug 2009,
BrSalgado
)
(raw view)
E
dit
A
ttach
---+!! Papo de botequim parte XI --- %TOC% --- - E aí rapaz, tudo bom? - Belê, mas você lembra que me pediu para fazer um programa que quando o tamanho de uma tela variasse, no seu centro apareceria dinamicamente, em vídeo reverso, a quantidade de linhas e colunas do jeito que o Linux faz normalmente? Pois é eu fiz mas a aparência não ficou igual. - Não estou nem aí para a aparência, o que eu queria é que você exercitasse o que aprendemos. Deixe-me ver o que você fez. %TERMINAL_INI% $ cat tamtela.sh%OUT_INI% #!/bin/bash # # Coloca no centro da tela, em video reverso, # a quantidade de colunas e linhas # quando o tamanho da tela eh alterado. # trap Muda 28 # 28 = sinal gerado pela mudanca no tamanho # da tela e Muda eh a funcao que fara isso. Bold=$(tput bold) # Modo de enfase Rev=$(tput rev) # Modo de video reverso Norm=$(tput sgr0) # Restaura a tela ao padrao default Muda () { clear Cols=$(tput cols) Lins=$(tput lines) tput cup $(($Lins / 2)) $(((Cols - 7) / 2)) # Centro da tela echo $Bold$Rev$Cols X $Lins$Norm } clear read -n1 -p "Mude o tamanho da tela ou tecle algo para terminar "%OUT_FIM% %TERMINAL_FIM% - Perfeito! Que se dane a aparência, depois vou te ensinar uns macetes para melhorá-la o que vale é que o programa está funcionando e está bem enxuto. - Poxa, perdi o maior tempo tentando descobrir como aumentar o fonte ... - Deixe isso para lá e hoje vamos ver umas coisas bastante interessantes e úteis. ---++ Named Pipes Um outro tipo de _pipe_ é o =named pipe=, que também é chamado de =FIFO=. =FIFO= é um acrônimo de _<kbd>F</kbd>irst <kbd>I</kbd>n <kbd>F</kbd>irst <kbd>O</kbd>ut_ 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: %TERMINAL_INI% $ ls -l pipe1%OUT_INI% prw-r-r-- 1 julio dipao 0 Jan 22 23:11 pipe1|%OUT_FIM% %TERMINAL_FIM% O =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: %TERMINAL_INI% $ mkfifo pipe1 %TERMINAL_FIM% Como sempre, a melhor forma de mostrar como algo funciona é dando exemplos. Suponha que nós tenhamos criado o =named pipe= mostrado anteriormente. Vamos agora trabalhar com duas sessões ou duas consoles virtuais ou uma de cada. Em uma delas faça: %TERMINAL_INI% $ ls -l > pipe1 %TERMINAL_FIM% e em outra faça: %TERMINAL_INI% $ cat < pipe1 %TERMINAL_FIM% _Voilá_! A saída do comando executado na primeira console foi exibida na segunda. Note que a ordem em que os comandos ocorreram não importa. Se você prestou atenção, reparou que o primeiro comando executado, parecia ter "pendurado, congelado". Isto acontece porque a outra ponta do _pipe_ ainda não estava conectada, e então o sistema operacional suspendeu o primeiro processo até que o segundo "abrisse" o _pipe_. Para que um processo que usa _pipe_ não fique em modo de _wait_, é necessário que em uma ponta do _pipe_ tenha um processo "tagarela" e na outra um "ouvinte" e no exemplo que demos, o =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. ---+++ Sincronização de processos. Suponha que você dispare paralelamente dois programas (processos) cujos diagramas de blocos de suas rotinas são como a figura a seguir: <center> <img src="%PUBURL%/%WEB%/FreeSkinImagens/imagem.GIF" alt="Gráfico do Named Pipe" /> </center> Os dois processos são disparados em paralelo e no =BLOCO1= do =Programa1= as três classificações são disparadas da seguinte maneira: <verbatim> 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 } ... </verbatim> 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=: <verbatim> OK=`cat pipe1` if [ $OK = va ] then ... Rotina de impressão ... else # Recebeu "pare" em OK exit 1 fi </verbatim> 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. ---+++ Bloqueio de arquivos Suponha que você escreveu uma CGI (_<kbd>C</kbd>ommon <kbd>G</kbd>ateway <kbd>I</kbd>nterface_) em _Shell_ para contar quantos _hits_ recebe uma determinada URL e a rotina de contagem está da seguinte maneira: <verbatim> Hits="$(cat page.hits 2> /dev/null)" || Hits=0 echo $((Hits=Hits++)) > page.hits </verbatim> 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. %TERMINAL_INI% $ cat contahits.sh%OUT_INI% #!/bin/bash PIPE="/tmp/pipe_contador" # arquivo named pipe # dir onde serao colocados os arquivos contadores de cada pagina DIR="/var/www/contador" [ -p "$PIPE" ] || mkfifo "$PIPE" while : do for URL in $(cat < $PIPE) do FILE="$DIR/$(echo $URL | sed 's,.*/,,')" # OBS1: no sed acima, como precisava procurar # uma barra,usamos vírgula como separador. # OBS2: quando rodar como daemon comente a proxima linha echo "arquivo = $FILE" n="$(cat $FILE 2> /dev/null)" || n=0 echo $((n=n+1)) > "$FILE" done done%OUT_FIM% %TERMINAL_FIM% Como só este script altera os arquivos, não existe problema de concorrência. Este script será um _daemon_, isto é, rodará em _background_. Quando uma página sofrer um acesso, ela escreverá a sua URL no arquivo de _pipe_. Para testar, execute este comando: <verbatim> echo "teste_pagina.html" > /tmp/pipe_contador </verbatim> Para evitar erros, em cada página que quisermos adicionar o contador acrescentamos a seguinte linha: <verbatim> <!--#exec cmd="echo $REQUEST_URI > /tmp/pipe_contador"--> </verbatim> 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 (<a href="http://www.google.com.br/search?hl=pt-BR&q=%22thobias+salazar+trevisan%22+%22Seja+bem-vindo%22&btnG=Pesquisar&meta=lr%3Dlang_pt" target="_blank">Dê uma olhada e inclua-a nos favoritos</a>). Ahhh! Você pensa que o assunto sobre =named pipes= está esgotado? Enganou-se. Vou mostrar um uso diferente a partir de agora. ---+++ Substituição de processos Acabei de mostrar um monte de dicas sobre =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: %TERMINAL_INI% $ cat <(ls -l) %TERMINAL_FIM% Resultará no 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: %TERMINAL_INI% $ ls -l >(cat) l-wx------ 1 jneves jneves 64 Aug 27 12:26 /dev/fd/63 -> pipe:[7050] %TERMINAL_FIM% É... Realmente é um =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: %TERMINAL_INI% $ cmp <(cat dir/*) <(cat dir.bkp/*) || echo backup furado %TERMINAL_FIM% ou, melhor ainda: %TERMINAL_INI% $ cmp <(cat dir/*) <(cat dir.bkp/*) >/dev/null || echo backup furado %TERMINAL_FIM% Da forma acima, a comparação foi efetuada em todas as linhas de todos os arquivos de ambos os diretórios. Para acelerar o processo, poderíamos compara somente a listagem longa de ambos os diretórios, pois qualquer modificação que um arquivo sofra, é mostrada na data/hora de alteração e/ou no tamanho do arquivo. Veja como ficaria: %TERMINAL_INI% $ cmp <(ls -l dir) <(ls -l dir.bkp) >/dev/null || echo backup furado %TERMINAL_FIM% Este é um exemplo meramente didático, mas são tantos os comandos que produzem mais de uma linha de saída, que serve como guia para outros. Eu quero gerar uma listagem dos meus arquivos, numerando-os e ao final dar o total de arquivos do diretório corrente: <verbatim> while read arq do ((i++)) # assim nao eh necessario inicializar i echo "$i: $arq" done < <(ls) echo "No diretorio corrente (`pwd`) existem $i arquivos" </verbatim> 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: <verbatim> ls | while read arq do ((i++)) # assim nao eh necessario inicializar i echo "$i: $arq" done echo "No diretorio corrente (`pwd`) existem $i arquivos" </verbatim> 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: <verbatim> #!/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: </verbatim> 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: %TERMINAL_INI% $ ls | ln %TERMINAL_FIM% ou então, usando a própria substituição de processos: %TERMINAL_INI% $ cat -n <(ls) %TERMINAL_FIM% Um último exemplo: você deseja comparar =arq1= e =arq2= usando o comando =comm=, mas este comando necessita que os arquivos estejam classificados. Então a melhor forma de proceder é: %TERMINAL_INI% $ comm <(sort arq1) <(sort arq2) %TERMINAL_FIM% Esta forma evita que você faça as seguintes operações: %TERMINAL_INI% $ sort arq1 > /tmp/sort1 $ sort arq2 > /tmp/sort2 $ comm /tmp/sort1 /tmp/sort2 $ rm -f /tmp/sort1 /tmp/sort2 %TERMINAL_FIM% Pessoal, o nosso Papo de Botequim chegou ao fim :( . 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 <a href="mailto:contato@clavis.com.br?Subject=Curso de Shell com Julio Neves">gerencia de treinamento</a> 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 <a href="mailto:julioneves@openoffice.org?Subject=Dúvidas Papo de Botequim">mim</a>. Valeu!
E
dit
|
A
ttach
|
P
rint version
|
H
istory
: r8
<
r7
<
r6
<
r5
<
r4
|
B
acklinks
|
V
iew topic
|
M
ore topic actions
Topic revision: r8 - 01 Aug 2009 - 22:34:18 -
BrSalgado
TWikiBar
Página Inicial
Últimas alterações
Índice
Procurar
Estatísticas de Uso
Aviso de Atualização
Configurações Gerais
Projeto Gráfico
Mapa do Site
Quem Somos
Registre-se
?
Regras de Formatação
Biblioteca Gráfica
?
Carinhas Gráficas
Webs Wiki-SL
Amadeu
Anapolivre
ArquivoLivre
Arte
BahiaSocial
BeaBa
BibliotecaLivre
Blogs
BrasilDigital
BrasilELivre
BSM
Ccsa
CESL
CoberturaWiki
Cooperativas
Curriculo
DarvinMarosin
DiaD
Dinamicoop
Economia
EconomiaSolidaria
EducacaoLivre
Ekaaty
Emacsbr
ENSL
Fatos
Festival3
Festival4
Flisol
Fmpb
Formatos
Foswikibr
FSM2005
GNOMEBR
GTTemario2004
GTWeb
Guialivre
HDC
Incubus
InkscapeBrasil
Jogos
KdeBR
KSP
LGM
LinuxStokDoc
Livros
Main
Mentores
MHHOB
MinuanoDigital
MoradiaECidadania
OlhosDagua
Olimpo
OLPC
OOPTQ
Papers
PCLivre
PentahoBrasil
Pessoas
Portal
Prefeituras
PSLAL
PSLBA
PSLBancarios
PSLBrasil
PSLGO
PSLMA
PSLMG
PSLMIP
PSLMT
PSLMulheres
PSLPI
PubFisl10
PubFisl7
PubFisl8
PubFisl9
QuilomboDoSopapo
RadioSL
RedeMesh
RedePopular
RobotWars
Sandbox
Saudelivre
Scribus
Sementes
Shakya
SLRJ
SoftwareLivreIrece
SoftwareLivreVS
SoLiSC
SuporteLivre
System
Telecentros
TeseSA
TextoLivre
TV
TWikiBar
TWikiPtbr
UNELivre
UNIMIX
VilaTorres
WebNordeste
WTRD2004
Este Menu
?skin=free
English
Español
Português brasileiro
Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki-SL?
Send feedback