IT Infrastructure Analyst um Tribunal de Justiça de Pernambuco
Melden
Technologie
Blocos de código e laços (ou loops, como preferem alguns) são o tema do mês em mais uma lição de nosso curso de Shell Script. Garçom, salta uma boa redondinha, que tô a fim de refrescar o pensamento!
1. ���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim
Curso de Shell Script
Papo de
Botequim VI
Blocos de código e laços (ou loops, como preferem alguns)
Dave Hamilton - www.sxc.hu
são o tema do mês em mais uma lição de nosso curso de Shell
Script. Garçom, salta uma boa redondinha, que tô a fim de
refrescar o pensamento! POR JULIO CEZAR NEVES
F ala, cara! E aí, já tá sabendo tudo
do comando for? Eu te deixei um
exercício para treinar, se não me
engano era para contar a quantidade de
palavras de um arquivo... Você fez?
Ou seja, o programa começa, como
sempre, verificando se a passagem de
parâmetros foi correta; em seguida o
comando for se incumbe de pegar cada
uma das palavras (lembre-se que o $IFS
Uma vez que chegamos neste ponto,
creio ser interessante citar que o Shell
trabalha com o conceito de “Expansão
Aritmética” (Arithmetic Expansion),
que é acionada por uma construção da
– Claro! Tô empolgadão com essa lin- padrão é branco, TAB e ENTER, que é forma $((expressão)) ou let expressão.
guagem! Eu fi z da forma que você exatamente o que desejamos para sepa- No último loop for usei a expansão
pediu, olha só... rar as palavras), incrementando a vari- aritmética das duas formas, mas não
– Êpa! Peraí que eu tô sequinho pra ável $Cont. Vamos relembrar como é o podemos seguir adiante sem saber que
tomar um chope. Aê Chico, traz dois arquivo ArqDoDOS.txt. a expressão pode ser uma das listadas
por favor. Um sem colarinho! na tabela 1.
– Como eu ia dizendo, olha como eu fi z. $ cat ArqDoDOS.txt Mas você pensa que o papo de loop
É muito fácil... Este arquivo (ou laço) se encerra no comando for?
foi gerado pelo Ledo engano, amigo, vamos a partir de
$ cat contpal.sh DOS/Rwin e foi agora ver mais dois comandos.
#!/bin/bash baixado por um
# Script meramente pedagógico ftp mal feito. O comando while
# cuja função é contar a Todos os programadores conhecem este
# quantidade de palavras de Agora vamos testar o programa pas- comando, porque é comum a todas as
# um arquivo. Supõe-se que as sando esse arquivo como parâmetro: linguagens. Nelas, o que normalmente
# palavras estão separadas ocorre é que um bloco de comandos é
# entre si por espaços, <TAB> $ contpal.sh ArqDoDOS.txt executado, enquanto (enquanto, em
# ou <ENTER>. O arquivo ArqDoDOS.txt tem 14 inglês, é “while”) uma determinada
if [ $# -ne 1 ] palavras. condição for verdadeira.
then
echo uso: $0 /caminho/do/ U Funcionou legal! Tabela 1: Expressões no Shell
arquivo Se você se lembra, Expressão Resultado
exit 2 em nossa última id++ id-- pós-incremento e pós-decremento de variáveis
fi aula mostramos o ++id --id pré-incremento e pré-decremento de variáveis
Cont=0 loop for a seguir: ** exponenciação
for Palavra in $(cat $1) */% multiplicação, divisão, resto da divisão (módulo)
do for ((; i<=9;)) +- adição, subtração
Cont=$((Cont+1)) do <= >= < > comparação
== != igualdade, desigualdade
done let i++
&& E lógico
echo O arquivo $1 tem $Cont U echo -n "$i "
|| OU lógico
palavras. done
�����������������������������
86 edição 06 www.linuxmagazine.com.br
��������������������������������������������������������������������������������
2. ����������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER
Pois bem, isso é o que acontece nas $ logaute.sh $cat monbg.sh
linguagens caretas! Em programação xefe pts/0 Jan 4 08:46 U #!/bin/bash
Shell, o bloco de comandos é executado (10.2.4.144) # Executa e monitora um
enquanto um comando for verdadeiro. xefe pts/0 Jan 4 08:46 U # processo em background
E é claro, se quiser testar uma condi- (10.2.4.144) $1 & # Coloca em backgroud
ção, use o comando while junto com o ... while ps | grep -q $!
comando test, exatamente como você xefe pts/0 Jan 4 08:46 U do
aprendeu a fazer no if, lembra? Então a (10.2.4.144) sleep 5
sintaxe do comando fica assim: done
Isto é, a cada 30 segundos a saída do echo Fim do Processo $1
while comando comando grep seria enviada para a tela,
do o que não é legal, já que poluiria a tela Esse script é bastante similar ao ante-
cmd1 do meu micro e a mensagem tão espe- rior, mas tem uns macetes a mais, veja
cmd2 rada poderia passar despercebida. Para só: ele tem que ser executado em back-
... evitar isso, já sabemos que a saída do ground para não prender o prompt mas
cmdn pipeline tem que ser redirecionada para o $! será o do programa passado como
done o dispositivo /dev/null. parâmetro, já que ele foi colocado em
background após o monbg.sh propria-
e dessa forma, o bloco formado pelas $ cat logaute.sh mente dito. Repare também na opção -q
instruções cmd1, cmd2,... e cmdn é exe- #!/bin/bash (quiet) do grep, que serve para fazê-lo
cutado enquanto a execução da instru- # Espero que a Xuxa não tenha “trabalhar em silêncio”. O mesmo resul-
ção comando for bem sucedida. # copyright de xefe e xato :) tado poderia ser obtido com a linha:
Suponha a seguinte cena: tinha while who | grep xefe > /dev/null while ps | grep $! > /dev/null, como nos
uma tremenda gata me esperando e do exemplos que vimos até agora.
eu estava preso no trabalho sem poder sleep 30 Vamos melhorar o nosso velho
sair porque o meu chefe, que é um pé done musinc, nosso programa para incluir
no saco (aliás chefe-chato é uma redun- echo O xato se mandou, não U registros no arquivo musicas, mas
dância, né?), ainda estava na sala dele, hesite, dê exit e vá a luta antes preciso te ensinar a pegar
que fica bem na minha passagem para a um dado da tela, e já vou avisando:
rua. Ele começou a ficar cabreiro depois Agora quero montar um script que só vou dar uma pequena dica do
da quinta vez que passei pela sua porta receba o nome (e eventuais parâme- comando read (que é quem pega o
e olhei para ver se já havia ido embora. tros) de um programa que será execu- dado da tela), que seja o suficiente
Então voltei para a minha mesa e fi z, no tado em background e que me informe para resolver este nosso problema.
servidor, um script assim: do seu término. Mas, para você enten- Em uma outra rodada de chope vou
der este exemplo, primeiro tenho de te ensinar tudo sobre o assunto,
$ cat logaute.sh mostrar uma nova variável do sistema. inclusive como formatar tela, mas
#!/bin/bash Veja estes comandos executados direta- hoje estamos falando sobre loops. A
# Espero que a Xuxa não tenha mente no prompt: sintaxe do comando read que nos
# copyright de xefe e xato :) interessa por hoje é a seguinte:
while who | grep xefe $ sleep 10&
do [1] 16317 $ read -p "prompt de leitura" var
sleep 30 $ echo $!
done 16317 Onde “prompt de leitura” é o texto
echo O xato se mandou, não U [1]+ Done sleep 10 que você quer que apareça escrito na
hesite, dê exit e vá à luta $ echo $! tela. Quando o operador teclar tal dado,
16317 ele será armazenado na variável var.
Neste scriptzinho, o comando while Por exemplo:
testa o pipeline composto pelos coman- Isto é, criei um processo em back-
dos who e grep, que será verdadeiro ground que dorme por 10 segundos, $ read -p "Título do Álbum: " Tit
enquanto o grep localizar a palavra somente para mostrar que a variável $!
xefe na saída do comando who. Desta guarda o PID (Process ID) do último pro- Bem, uma vez entendido isso, vamos
forma, o script dormirá por 30 segundos cesso em background. Mas observe a à especificação do nosso problema:
enquanto o chefe estiver logado (Argh!). listagem e repare, após a linha do Done, faremos um programa que inicialmente
Assim que ele se desconectar do servi- que a variável reteve o valor mesmo lerá o nome do álbum e em seguida fará
dor, o fluxo do script sairá do loop e te após o término desse processo. um loop de leitura, pegando o nome da
mostrará a tão ansiada mensagem de Bem, sabendo isso, já fica mais fácil música e o artista. Esse loop termina
liberdade. Mas quando executei o script, monitorar qualquer processo em back- quando for informada uma música com
adivinha o que aconteceu? ground. Veja só como: nome vazio, isto é, quando o operador
�����������������������������
www.linuxmagazine.com.br edição 06 87
��������������������������������������������������������������������������������
3. ���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim
Dica until comando $cat chegada.sh
Leitura de arquivo significa ler um a um do #!/bin/bash
todos os registros, o que é sempre uma cmd1 until who | grep julio
operação lenta. Fique atento para não usar cmd2 do
o while quando for desnecessário. O Shell ... sleep 30
tem ferramentas como o sed e a família cmdn done
grep, que vasculham arquivos de forma done echo $(date "+ Em %d/%m às U
otimizada sem que seja necessário o uso do %H:%Mh") > relapso.log
while para fazê-lo registro a registro.
e dessa forma o bloco de comandos for-
mado pelas instruções cmd1, cmd2,... e Olha que safado! O cara estava mon-
der um simples <ENTER>. Para facili- cmdn é executado até que a execução da tando um log com os meus horários de
tar a vida do operador, vamos oferecer instrução comando seja bem sucedida. chegada, e ainda por cima chamou o
como default o mesmo nome do artista Como eu te disse, while e until funcio- arquivo de relapso.log! O que será que
da música anterior (já que é normal que nam de forma antagônica, e isso é muito ele quis dizer com isso?
o álbum seja todo do mesmo artista) até fácil de demonstrar: em uma guerra, Nesse script, o pipeline who | grep
que ele deseje alterá-lo. Veja na listagem sempre que se inventa uma arma, o julio, será bem sucedido somente
1 como ficou o programa. inimigo busca uma solução para neu- quando julio for encontrado na saída
Nosso exemplo começa com a lei- tralizá-la. Foi baseado nesse principio do comando who, isto é, quando eu
tura do título do álbum. Caso ele não belicoso que meu chefe desenvolveu, no me “logar” no servidor. Até que isso
seja informado, terminamos a execu- mesmo servidor em que eu executava o aconteça, o comando sleep, que forma
ção do programa. Em seguida um grep logaute.sh, um script para controlar o o bloco de instruções do until, colocará
procura, no início (^) de cada registro meu horário de chegada. o programa em espera por 30 segun-
de músicas, o título informado seguido Um dia tivemos um problema na rede. dos. Quando esse loop encerrar-se, será
do separador (^) (que está precedido de Ele me pediu para dar uma olhada no enviada uma mensagem para o arquivo
uma contrabarra [] para protegê-lo da micro dele e me deixou sozinho na sala. relapso.log. Supondo que no dia 20/01
interpretação do Shell). Resolvi bisbilhotar os arquivos – guerra eu me “loguei” às 11:23 horas, a mensa-
Para ler os nomes dos artistas e as é guerra – e veja só o que descobri: gem seria a seguinte:
músicas do álbum, foi montado um loop
while simples, cujo único destaque é o Listagem 1
fato de ele armazenar o nome do intér-
$ cat musinc.sh
prete da música anterior na variável
#!/bin/bash
$oArt, que só terá o seu conteúdo alte-
# Cadastra CDs (versao 4)
rado quando algum dado for informado
#
para a variável $Art, isto é, quando não
clear
for teclado um simples ENTER para
read -p "Título do Álbum: " Tit
manter o artista anterior.
[ "$Tit" ] || exit 1 # Fim da execução se título vazio
O que foi visto até agora sobre o while
if grep "^$Tit^" musicas > /dev/null
foi muito pouco. Esse comando é muito
then
utilizado, principalmente para leitura
echo "Este álbum já está cadastrado"
de arquivos, porém ainda nos falta
exit 1
bagagem para prosseguir. Depois que
fi
aprendermos mais sobre isso, veremos
Reg="$Tit^"
essa instrução mais a fundo.
Cont=1
O comando until oArt=
while true
Este comando funciona de forma
do
idêntica ao while, porém ao contrário.
echo "Dados da trilha $Cont:"
Disse tudo mas não disse nada, né? É
read -p "Música: " Mus
o seguinte: ambos testam comandos;
[ "$Mus" ] || break # Sai se vazio
ambos possuem a mesma sintaxe e
read -p "Artista: $oArt // " Art
ambos atuam em loop; porém, o while
[ "$Art" ] && oArt="$Art" # Se vazio Art anterior
executa o bloco de instruções do loop
Reg="$Reg$oArt~$Mus:" # Montando registro
enquanto um comando for bem suce-
Cont=$((Cont + 1))
dido; já o until executa o bloco do loop
# A linha anterior tb poderia ser ((Cont++))
até que o comando seja bem sucedido.
done
Parece pouca coisa, mas a diferença é
echo "$Reg" >> musicas
fundamental. A sintaxe do comando é
sort musicas -o musicas
praticamente a mesma do while. Veja:
�����������������������������
88 edição 06 www.linuxmagazine.com.br
��������������������������������������������������������������������������������
4. ����������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER
Em 20/01 às 11:23h
Atalhos no
loop �� ��
Voltando à nossa CDteca, quando Nem sempre um �������� ��������
vamos cadastrar músicas seria ideal ciclo de programa,
que pudéssemos cadastrar diversos compreendido
CDs de uma vez só. Na última versão entre um do e um ����� ����� ����� ��������
do programa isso não ocorre: a cada CD done, sai pela porta
cadastrado o programa termina. Veja na da frente. Em algu-
�������� ��������
listagem 2 como melhorá-lo. mas oportunidades,
Nesta versão, um loop maior foi adi- temos que colocar
���� ����
cionado antes da leitura do título, que um comando que
só terminará quando a variável $Para aborte de forma
deixar de ser vazia. Caso o título do c ont rolada esse �������������������� �����������������������
álbum não seja informado, a variá- loop. De maneira Figura 1: A estrutura dos comandos break e continue, usados para contro-
vel $Para receberá um valor (coloquei inversa, algumas lar o fluxo de execução em loops.
1, mas poderia ter colocado qualquer vezes desejamos
coisa) para sair desse loop, terminando que o f lu xo de pectivamente os comandos break (que
o programa. No resto, o script é idênticoexecução do programa volte antes de já vimos rapidamente nos exemplos do
à versão anterior. chegar ao done. Para isso, temos res- comando while) e continue, que funcio-
nam da forma mostrada na figura 1.
Listagem 2 O que eu não havia dito anterior-
mente é que nas suas sintaxes genéricas
$ cat musinc.sh
eles aparecem da seguinte forma:
#!/bin/bash
# Cadastra CDs (versao 5)
break [qtd loop]
#
Para=
e também:
until [ "$Para" ]
do
continue [qtd loop]
clear
read -p "Título do Álbum: " Tit
Onde qtd loop representa a quanti-
if [ ! "$Tit" ] # Se titulo vazio...
dade dos loops mais internos sobre os
then
quais os comandos irão atuar. Seu valor
Para=1 # Liguei flag de saída
por default é 1.
else
Duvido que você nunca tenha apa-
if grep "^$Tit^" musicas > /dev/null
gado um arquivo e logo após deu um
then
tabefe na testa se xingando porque
echo "Este álbum já está cadastrado"
não devia tê-lo removido. Pois é, na
exit 1
décima vez que fi z esta besteira, criei
fi
um script para simular uma lixeira,
Reg="$Tit^"
isto é, quando mando remover um (ou
Cont=1
vários) arquivo(s), o programa “fi nge”
oArt=
que deletou, mas no duro o que ele fez
while [ "$Tit" ]
foi mandá-lo(s) para o diretório /tmp/
do
LoginName_do_usuario. Chamei esse
echo Dados da trilha $Cont:
programa de erreeme e no arquivo /etc/
read -p "Música: " Mus
profile coloquei a seguinte linha, que
[ "$Mus" ] || break # Sai se vazio
cria um “apelido” para ele:
read -p "Artista: $oArt // " Art
[ "$Art" ] && oArt="$Art" # Se vazio Art anterior
alias rm=erreeme
Reg="$Reg$oArt~$Mus:" # Montando registro
Cont=$((Cont + 1))
Veja o programa na listagem 3. Como
# A linha anterior tb poderia ser ((Cont++))
você pode ver, a maior parte do script
done
é formada por pequenas críticas aos
echo "$Reg" >> musicas
parâmetros informados, mas como o
sort musicas -o musicas
script pode ter recebido diversos arqui-
fi
vos a remover, a cada arquivo que não
done
se encaixa dentro do especificado há
�����������������������������
www.linuxmagazine.com.br edição 06 89
��������������������������������������������������������������������������������
5. ���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim
Listagem 3: erreeme.sh
$ cat erreeme.sh then
#!/bin/bash echo "$Arq nao existe."
# Erro=3
# Salvando cópia de um arquivo antes de removê-lo continue # Volta para o comando for
fi
# Tem de ter um ou mais arquivos a remover
if [ $# -eq 0 ] # Cmd. dirname informa nome do dir de $Arq
then DirOrig=`dirname $Arq`
echo "Erro -> Uso: erreeme arq [arq] ... [arq]" # Verifica permissão de gravacao no diretório
echo "O uso de metacaracteres e’ permitido. Ex.U if [ ! -w $DirOrig ]
erreeme arq*" then
exit 1 echo "Sem permissão no diretorio de $Arq"
fi Erro=4
continue # Volta para o comando for
# Variável do sistema que contém o nome do usuário. fi
MeuDir="/tmp/$LOGNAME"
# Se não existir o meu diretório sob o /tmp... # Se estou "esvaziando a lixeira"...
if [ ! -d $MeuDir ] if [ "$DirOrig" = "$MeuDir" ]
then then
mkdir $MeuDir # Vou criá-lo echo "$Arq ficara sem copia de seguranca"
fi rm -i $Arq # Pergunta antes de remover
# Será que o usuário removeu?
# Se não posso gravar no diretório... [ -f $Arq ] || echo "$Arquivo removido"
if [ ! -w $MeuDir ] continue
then fi
echo "Impossivel salvar arquivos em $MeuDir. U
Mude as permissões..." # Guardo no fim do arquivo o seu diretório originalU
exit 2 para usá-lo em um script de undelete
fi cd $DirOrig
pwd >> $Arq
# Variável que indica o cod. de retorno do programa mv $Arq $MeuDir # Salvo e removo
Erro=0 echo "$Arq removido"
# Um for sem o in recebe os parametros passados done
for Arq
do # Passo eventual número do erro para o código
# Se este arquivo não existir... # de retorno
if [ ! -f $Arq ] exit $Erro
um continue, para que a seqüência volte faça-o em casa e me traga para dis- um email para julio.neves@gmail.
para o loop do for de forma a receber cutirmos no nosso próximo encontro com. Agora chega de papo que eu já
outros arquivos. aqui no boteco. estou de goela seca de tanto falar. Me
Quando você está no Windows (com – Poxa, mas nesse eu acho que vou dan- acompanha no próximo chope ou já
perdão da má palavra) e tenta remover çar, pois não sei nem como começar... vai sair correndo para fazer o script
aquele monte de lixo com nomes esqui- – Cara, este programa é como tudo que passei?
sitos como HD04TG.TMP, se der erro o que se faz em Shell: extrema- – Deixa eu pensar um pouco...
em um dos arquivos os outros não são mente fácil. É para ser feito em, no – Chico, traz mais um chope enquanto
removidos, não é? Então, o continue foi máximo, 10 linhas. Não se esqueça ele pensa!
usado para evitar que uma improprie- de que o arquivo está salvo em /tmp/
dade dessas ocorra, isto é, mesmo que $LOGNAME e que sua última linha é Julio Cezar Neves é Analista de
SOBRE O AUTOR
dê erro na remoção de um arquivo, o o diretório em que ele residia antes Suporte de Sistemas desde 1969 e tra-
programa continuará removendo os de ser “removido”. Também não se balha com Unix desde 1980, quando
outros que foram passados. esqueça de criticar se foi passado o participou do desenvolvimento do
– Eu acho que a esta altura você deve nome do arquivo a ser removido. SOX, um sistema operacional similar
estar curioso para ver o programa – É eu vou tentar, mas sei não... ao Unix produzido pela Cobra Com-
putadores. Pode ser contatado no
que restaura o arquivo removido, não – Tenha fé, irmão, eu tô te falando que
e-mail julio.neves@gmail.com
é? Pois então aí vai vai um desafio: é mole! Qualquer dúvida é só passar
�����������������������������
90 edição 06 www.linuxmagazine.com.br
��������������������������������������������������������������������������������