You are here:
Wiki-SL
>
TWikiBar Web
>
BatePapos
>
TWikiBarPapo005
(01 Aug 2009,
BrSalgado
)
(raw view)
E
dit
A
ttach
---+!! Papo de Botequim Parte V %TOC% - Fala cara! E as idéias estão em ordem? Já fundiu a cuca ou você ainda aguenta mais _Shell_? - Guento! Tô gostando muito! Gostei tanto que até caprichei no exercício que você passou. Lembra que você me pediu para fazer um programa que receberia como parâmetro o nome de um arquivo e que quando executado salvaria este arquivo com o nome original seguido de um til (=~=) e colocaria este arquivo dentro do =vi=? - Claro que lembro, me mostre e explique como você fez. %TERMINAL_INI% $ cat vira%OUT_INI% #!/bin/bash # # vira - vi resguardando arquivo anterior # == = = # Verificando se foi passado 1 parametro if [ "$#" -ne 1 ] then echo "Erro -> Uso: $0 <arquivo>" exit 1 fi Arq=$1 # Caso o arquivo não exista, nao ha copia para ser salva if [ ! -f "$Arq" ] then vi $Arq exit 0 fi # Se nao puder alterar o arquivo vou usar o vi para que? if [ ! -w "$Arq" ] then echo "Voce nao tem direito de gravacao em $Arq" exit 2 fi # Ja que esta tudo OK, vou salvar a copia e chamar o vi cp -f $Arq $Arq~ vi $Arq exit 0%OUT_FIM% %TERMINAL_FIM% - É, beleza! Mas me diz uma coisa: porque você terminou o programa com um =exit 0=? - Ahhh! Eu descobri que o número após o =exit= resultará no código de retorno do programa (o =$?=, lembra?), e desta forma, como foi tudo bem sucedido, ele encerraria com o =$? = 0=. Porém se você observar, verá que caso o programa não tenha recebido o nome do arquivo ou caso o operador não tivesse direito de gravação sobre este arquivo, o código de retorno (=$?=) seria diferente do zero. - Grande garoto, aprendeu legal, mas é bom deixar claro que =exit 0=, simplesmente =exit= ou não colocar =exit=, produzem igualmente um código de retorno (=$?=) igual a zero. Agora vamos falar sobre as instruções de _loop_ ou laço, mas antes vou passar o conceito de bloco de programa. Até agora já vimos alguns blocos de programa. Quando te mostrei um exemplo para fazer um =cd= para dentro de um diretório que era assim: <verbatim> cd lmb 2> /dev/null || { mkdir lmb cd lmb } </verbatim> O fragmento contido entre as duas chaves (={}=), forma um bloco de comandos. Também neste exercício que acabamos de ver, em que salvamos o arquivo antes de editá-lo, existem vários blocos de comandos compreendidos entre os =then= e os =fi= do =if=. Um bloco de comandos também pode estar dentro de um =case=, ou entre um =do= e um =done=. - Peraí Julio, que =do= e =done= é esse, não me lembro de você ter falado nisso e olha estou prestando muita atenção... - Pois é, ainda não havia falado porque não havia chegado o momento propício. Todas as instruções de _loop_ ou laço, executam os comandos do bloco compreendido entre o =do= e o =done=. ---++ Comandos de _Loop_ (ou laço) As instruções de _loop_ ou laço são o =for=, o =while= e o =until= que passarei a te explicar uma-a-uma a partir de agora. ---+++ O comando for Se você está habituado a programar, certamente já conhece o comando =for=, mas o que você não sabe é que o =for=, que é uma instrução intrinseca do _Shell_ (isto significa que o código fonte do comando faz parte do código fonte do _Shell_, ou seja em bom programês é um _built-in_), é muito mais poderoso que os seus correlatos das outras linguagens. Vamos entender a sua sintaxe, primeiramente em português e depois como funciona no duro. <verbatim> para var em val1 val2 ... valn faça cmd1 cmd2 cmdn feito </verbatim> Onde a variável =var= assume cada um dos valores da lista =val1 val2 ... valn= e para cada um desses valores executa o bloco de comandos formado por =cmd1=, =cmd2= e =cmdn= Agora que já vimos o significado da instrução em português, vejamos a sintaxe correta: ---++++ Primeira sintaxe do comando for <verbatim> for var in val1 val2 ... valn do cmd1 cmd2 cmdn done </verbatim> Vamos direto para os exemplos, para entender direito o funcionamento deste comando. Vamos escrever um _script_ para listar todos os arquivos do nosso diretório separados por dois-pontos, mas primeiro veja: %TERMINAL_INI% $ echo *%OUT_INI% <nop>ArqDoDOS.txt1 confuso incusu logado musexc musicas musinc muslist%OUT_FIM% %TERMINAL_FIM% Isto é, o _Shell_ viu o asterisco (=*=) expandindo-o com o nome de todos os arquivos do diretório e o comando =echo= jogou-os para a tela separados por espaços em branco. Visto isso vamos ver como resolver o problema a que nos propuzemos: %TERMINAL_INI% $ cat testefor1%OUT_INI% #!/bin/bash # 1o. Prog didático para entender o for for Arq in * do echo -n $Arq: # A opcao -n eh para nao saltar linha done%OUT_FIM% %TERMINAL_FIM% Então vamos executá-lo: %TERMINAL_INI% $ testefor1%OUT_INI% <nop>ArqDoDOS.txt1:confuso:incusu:logado:musexc:musicas:musinc:muslist:%OUT_FIM%$ %TERMINAL_FIM% Como você viu o _Shell_ transformou o asterisco (que odeia ser chamado de asterístico) em uma lista de arquivos separados por espaços em branco. quando o =for= viu aquela lista, ele disse: "Opa, lista separadas por espaços é comigo mesmo!" O bloco de comandos a ser executado era somente o =echo=, que com a opção =-n= listou a variável =$Arq= seguida de dois-pontos (=:=), sem saltar a linha. O cifrão (=$=) do final da linha da execução é o _prompt_. que permaneceu na mesma linha também em função da opção =-n=. Outro exemplo simples (por enquanto): %TERMINAL_INI% $ cat testefor2%OUT_INI% #!/bin/bash # 2o. Prog didático para entender o for for Palavra in Papo de Botequim do echo $Palavra done%OUT_FIM% %TERMINAL_FIM% E executando vem: %TERMINAL_INI% $ testefor2%OUT_INI% Papo de Botequim%OUT_FIM% %TERMINAL_FIM% Como você viu, este exemplo é tão bobo e simples como o anterior, mas serve para mostrar o comportamento básico do =for=. Veja só a força do =for=: ainda estamos na primeira sintaxe do comando e já estou mostrando novas formas de usá-lo. Lá atrás eu havia falado que o =for= usava listas separadas por espaços em branco, mas isso é uma meia verdade, era só para facilitar a compreensão. ---+++++ Inter Field Separator Tabajara (IFS) No duro, as listas não são obrigatóriamente separadas por espaços mas antes de prosseguir, deixa eu te mostrar como se comporta uma variável do sistema chamada de =$IFS=. Repare seu conteúdo: %TERMINAL_INI% $ echo "$IFS" | od -h%OUT_INI% 0000000 0920 0a0a 0000004%OUT_FIM% %TERMINAL_FIM% Isto é, mandei a variável (protegida da interpretação do _Shell_ pelas aspas) para um _dump_ hexadecimal (=od -h=) e resultou: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Conteúdo da Variável $IFS* || | *Hexadecimal* | *Significado* | | =09= | =<TAB>= | | =20= | =<ESPAÇO>= | | =0a= | =<ENTER>= | </center> Onde o último =0a= foi proveniente do =<ENTER>= dado ao final do comando. Para melhorar a explicação, vamos ver isso de outra forma: %TERMINAL_INI% $ echo ":$IFS:" | cat -vet%OUT_INI% : ^I$ :$%OUT_FIM% %TERMINAL_FIM% Preste atenção na dica a seguir para entender a construção deste comando cat: %DICA_INI% No comando =cat=, a opção =-e= representa o =<ENTER>= como um cifrão (=$=) e a opção =-t= representa o =<TAB>= como um =^I=. Usei os dois-pontos (=:=) para mostrar o início e o fim do =echo=. E desta forma, mais uma vez pudemos notar que os três caracteres estão presentes naquela variável. %DICA_FIM% Agora veja você, =IFS= significa __Inter Field Separator__ ou, traduzindo, separador entre campos. Uma vez entendido isso, eu posso afirmar (porque vou provar) que o comando =for= não usa listas separadas por espaços em branco, mas sim pelo conteúdo da variável =$IFS=, cujo valor padrão (_default_) são esses caracteres que acabamos de ver. Para comprovarmos isso, vamos mostrar um _script_ que recebe o nome do artista como parâmetro e lista as músicas que ele executa, mas primeiramente vamos ver como está o nosso arquivo =musicas=: %TERMINAL_INI% $ cat musicas%OUT_INI% album 1^Artista1~Musica1:Artista2~Musica2 album 2^Artista3~Musica3:Artista4~Musica4 album 3^Artista5~Musica5:Artista6~Musica6 album 4^Artista7~Musica7:Artista1~Musica3 album 5^Artista9~Musica9:Artista10~Musica10%OUT_FIM% %TERMINAL_FIM% Em cima deste "leiaute" foi desenvolvido o _script_ a seguir: %TERMINAL_INI% $ cat listartista%OUT_INI% #!/bin/bash # Dado um artista, mostra as suas musicas if [ $# -ne 1 ] then echo Voce deveria ter passado um parametro exit 1 fi IFS=" :" for <nop>ArtMus in $(cut -f2 -d^ musicas) do echo "$ArtMus" | grep $1 && echo $ArtMus | cut -f2 -d~ done%OUT_FIM% %TERMINAL_FIM% O _script_, como sempre, começa testando se os parâmetros foram passados corretamente, em seguida o =IFS= foi setado para =<ENTER>= e dois-pontos (=:=) (como demonstram as aspas em linha diferentes), porque é ele que separa os blocos =Artistan~Musicam=. Desta forma, a variável =$ArtMus= irá receber cada um destes blocos do arquivo (repare que o =for= já recebe os registros sem o álbum em virtude do =cut= na sua linha). Caso encontre o parâmetro (=$1=) no bloco, o segundo =cut= listará somente o nome da música. Vamos executá-lo: %TERMINAL_INI% $ listartista Artista1%OUT_INI% Artista1~Musica1 Musica1 Artista1~Musica3 Musica3 Artista10~Musica10 Musica10%OUT_FIM% %TERMINAL_FIM% Êpa! Aconteceram duas coisas indesejáveis: os blocos também foram listados e a =Musica10= idem. Além do mais, o nosso arquivo de músicas está muito simples, na vida real, tanto a música quanto o artista têm mais de um nome. Suponha que o artista fosse uma dupla sertaneja chamada _Perereca & Peteleca_ (não gosto nem de dar a idéia com receio que isso se torne realidade:). Neste caso o =$1= seria Perereca e o resto deste lindo nome seria ignorado na pesquisa. Para que isso não ocorresse, eu deveia passar o nome do artista entre aspas (="=) ou alterar =$1= por =$@= (que significa todos os parâmetros passados), que é a melhor solução, mas neste caso eu teria que modificar a crítica dos parâmetros e o =grep=. A nova crítica não seria se eu passei um parâmetro, mas *pelo menos* um parâmetro e quanto ao =grep=, veja só o que resultaria após a substituição do =$*= (que entraria no lugar do =$1=) pelos parâmetros: <verbatim> echo "$ArtMus" | grep perereca & peteleca </verbatim> O que resultaria em erro. O correto seria: <verbatim> echo "$ArtMus" | grep -i "perereca & peteleca" </verbatim> Onde foi colocado a opção =-i= para que a pesquisa ignorasse maiúsculas e minúsculas e as aspas também foram inseridas para que o nome do artista fosse visto como uma só cadeia monolítica. Ainda falta consertar o erro dele ter listado o =Artista10=. Para isso o melhor é dizer ao =grep= que a cadeia está no início de =$ArtMus= (a expressão regular para dizer que está no início é =^=) e logo após vem um til (=~=). É necessário também que se redirecione a saída do grep para =/dev/null= para que os blocos não sejam mais listados. Veja então a nova (e definitiva) cara do programa: %TERMINAL_INI% $ cat listartista%OUT_INI% #!/bin/bash # Dado um artista, mostra as suas musicas # versao 2 if [ $# -eq 0 ] then echo Voce deveria ter passado pelo menos um parametro exit 1 fi IFS=" :" for <nop>ArtMus in $(cut -f2 -d^ musicas) do echo "$ArtMus" | grep -i "^$@~" > /dev/null && echo $ArtMus | cut -f2 -d~ done%OUT_FIM% %TERMINAL_FIM% Que executando vem: %TERMINAL_INI% $ listartista Artista1%OUT_INI% Musica1 Musica3%OUT_FIM% %TERMINAL_FIM% ---++++ Segunda sintaxe do comando for <verbatim> for var do cmd1 cmd2 cmdn done </verbatim> - Ué, sem o =in= como ele vai saber que valor assumir? - Pois é, né? Esta construção a primeira vista parece xquisita mas é bastante simples. Neste caso, =var= assumirá um-a-um cada um dos parâmetros passados para o progama. Vamos logo aos exemplos para entender melhor. Vamos fazer um _script_ que receba como parâmetro um monte de músicas e liste seus autores: %TERMINAL_INI% $ cat listamusica%OUT_INI% #!/bin/bash # Recebe parte dos nomes de musicas como parametro e # lista os interpretes. Se o nome for composto, deve # ser passado entre aspas. # ex. "Eu nao sou cachorro nao" "Churrasquinho de Mae" # if [ $# -eq 0 ] then echo Uso: $0 musica1 [musica2] ... [musican] exit 1 fi IFS=" :" for Musica do echo $Musica Str=$(grep -i "$Musica" musicas) || { echo " Não encontrada" continue } for <nop>ArtMus in $(echo "$Str" | cut -f2 -d^) do echo " $ArtMus" | grep -i "$Musica" | cut -f1 -d~ done done%OUT_FIM% %TERMINAL_FIM% Da mesma forma que os outros, começamos o exercício com uma crítica sobre os parâmetros recebidos, em seguida fizemos um =for= em que a variável =$Musica= receberá cada um dos parâmetros passados, colocando em =$Str= todos os álbuns que contém as músicas passadas. Em seguida, o outro =for= pega cada bloco =Artista~Musica= nos registros que estão em =$Str= e lista cada artista que execute aquela música. Como sempre vamos executá-lo para ver se funciona mesmo: %TERMINAL_INI% $ listamusica musica3 Musica4 "Eguinha Pocotó"%OUT_INI% musica3 Artista3 Artista1 Musica4 Artista4 Eguinha Pocotó Não encontrada%OUT_FIM% %TERMINAL_FIM% A listagem ficou feinha porque ainda não sabemos formatar a saída, mas qualquer dia desses, quando você souber posicionar o cursor, fazer negrito, trabalhar com cores e etc, faremos esta listagem novamente usando todas estas perfumarias e ela ficará muito _fashion_. A esta altura dos acontecimentos você deve estar se perguntando: "E aquele =for= tradicional das outras linguagens em que ele sai contando a partir de um número, com um determinado incremento até alcançar uma condição?" E é aí que eu te respondo: "Eu não te disse que o nosso =for= é mais porreta que os outros?" Para fazer isso existem duas formas: 1 - Com a primeira sintaxe que vimos, como nos exemplos a seguir direto no _prompt_: %TERMINAL_INI% $ for i in $(seq 9) > do > echo -n "$i " > done%OUT_INI% 1 2 3 4 5 6 7 8 9%OUT_FIM% %TERMINAL_FIM% Neste a variável =i= assumiu os inteiros de 1 a 9 gerados pelo comando =seq= e a opção =-n= do =echo= foi usada para não saltar linha a cada número listado (sinto-me ecologicamente correto por não gastar um monte de papel da revista quando isso pode ser evitado). Ainda usando o =for= com =seq=: %TERMINAL_INI% $ for i in $(seq 3 9) > do > echo -n "$i " > done%OUT_INI% 4 5 6 7 8 9%OUT_FIM% %TERMINAL_FIM% Ou ainda na forma mais completa do =seq=: %TERMINAL_INI% $ for i in $(seq 0 3 9) > do > echo -n "$i " > done%OUT_INI% 0 3 6 9%OUT_FIM% %TERMINAL_FIM% 2 – A outra forma de fazer o desejado é com uma sintaxe muito semelhante ao =for= da linguagem C, como veremos a seguir. ---++++ Terceira sintaxe do comando for <verbatim> for ((var=ini; cond; incr)) do cmd1 cmd2 cmdn done </verbatim> Onde: =var=ini= - Significa que a variável =var= começará de um valor inicial _ini_;%BR% =cond = - Siginifica que o _loop_ ou laço do =for= será executado enquanto =var= não atingir a condição =cond=;%BR% =incr = - Significa o incremento que a variável =var= sofrerá em cada passada do _loop_. Como sempre vamos aos exemplos que a coisa fica mais fácil: %TERMINAL_INI% $ for ((i=1; i<=9; i++)) > do > echo -n "$i " > done%OUT_INI% 1 2 3 4 5 6 7 8 9%OUT_FIM% %TERMINAL_FIM% Neste caso a variável =i= partiu do valor inicial =1=, o bloco de comando (neste caso somente o =echo=) será executado enquanto =i= menor ou igual (=<==) a =9= e o incremento de =i= será de =1= a cada passada do _loop_. Repare que no =for= propriamente dito (e não no bloco de comandos) não coloquei um cifrão (=$=) antes do =i=, e a notação para incrementar (=i++=) é diferente do que vimos até agora. Isto é porque o uso de parênteses duplos (assim como o comando =let=) chama o interpretador aritmético do _Shell_, que é mais tolerante. Como me referi ao comando =let=, só para mostrar como ele funciona e a versatilidade do =for=, vamos fazer a mesma coisa, porém omitindo a última parte do escopo do =for=, passando-a para o bloco de comandos. %TERMINAL_INI% $ for ((; i<=9;)) > do > let i++ > echo -n "$i " > done%OUT_INI% 1 2 3 4 5 6 7 8 9%OUT_FIM% %TERMINAL_FIM% Repare que o incremento saiu do corpo do =for= e passou para o bloco de comandos, repare também que quando usei o =let=, não foi necessário sequer inicializar a variável =$i=. Veja só os comandos a seguir dados diretamente no _prompt_ para mostrar o que acabo de falar: %TERMINAL_INI% $ echo $j $ let j++ $ echo $j%OUT_INI% 1%OUT_FIM% %TERMINAL_FIM% Ou seja, a variável =$j= sequer existia e no primeiro =let= assumiu o valor =0= (zero) para, após o incremento, ter o valor =1=. Veja só como as coisas ficam simples: %TERMINAL_INI% $ for arq in * > do > let i++ > echo "$i -> $Arq" > done%OUT_INI% 1 -> <nop>ArqDoDOS.txt1 2 -> confuso 3 -> incusu 4 -> listamusica 5 -> listartista 6 -> logado 7 -> musexc 8 -> musicas 9 -> musinc 10 -> muslist 11 -> testefor1 12 -> testefor2%OUT_FIM% %TERMINAL_FIM% - Pois é amigo, tenho certeza que você já tomou um xarope do comando =for=. Por hoje chega, na próxima vez que nos encontrarmos falaremos sobre outras instruções de _loop_, mas eu gostaria que até lá você fizesse um pequeno _script_ para contar a quantidade de palavras de um arquivo texto, cujo nome seria recebido por parâmetro. OBS: Essa contagem tem de ser feita usando o comando for para se habituar ao seu uso. Não vale usar o =wc -w=. - Aê Chico! Traz a saideira. 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
: r15
<
r14
<
r13
<
r12
<
r11
|
B
acklinks
|
V
iew topic
|
M
ore topic actions
Topic revision: r15 - 01 Aug 2009 - 22:32:56 -
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