You are here:
Wiki-SL
>
TWikiBar Web
>
BatePapos
>
TWikiBarTalk005
(23 May 2008,
DiogoFernandes
)
(raw view)
E
dit
A
ttach
---+!! Pub Talk Part V %TOC% - YO, man! How' doin' ? How's your brain ? Burned or can you handle more _Shell_ ? - I do ! I'm loving it ! I liked it so much that I put more love at exercise you gave me. Did you remember you asked me to do a program to receive as parameter a file name and, when executed, it should save this file with its original name followed by a tilde (=~=) and open the file with =vi= ? - Sure I do. Show me the money and tell me how you did it. %TERMINAL_INI% $ cat vira%OUT_INI% #!/bin/bash # # vira - vi backing up the previous file # == = = # Cheking if we have one parameter if [ "$#" -ne 1 ] then echo "Error -> Usage: $0 <arquivo>" exit 1 fi Arq=$1 # If the file doesn't exist, there is no copy to be saved if [ ! -f "$Arq" ] then vi $Arq exit 0 fi # If I can save the file, why call vi ? if [ ! -w "$Arq" ] then echo "You are not allowed to write $Arq" exit 2 fi # Everything is OK. I'll save the backup and call vi cp -f $Arq $Arq~ vi $Arq exit 0%OUT_FIM% %TERMINAL_FIM% - Yeah, right ! But tell me: why did you finish the program with an =exit 0=? - Ahhh! I found the number after =exit= will be the program's return code (the =$?=, remember?), and, as everything succeded ok, it would finish with =$? = 0=. But, if you notice, you'll see, in case of the file name doesn't be sent or the user didn't have the write privileges in this file, the return code would be different of zero. - Good boy, you learned cool, but is better to clarify =exit 0=, only =exit= or even don't put =exit=, make the same return code (=$?=) as zero. Now let me talk about _loop_ instructions. But, first things first, I'll thell you about program block concept. 'Till now, we saw some program blocks. When we saw an example about a =cd= inside a directory like this: <verbatim> cd lmb 2> /dev/null || { mkdir lmb cd lmb } </verbatim> the code inside two keys (={}=) makes a command block. Also in our last exercise, in which we saved a file before edit it, there are various command blocks inside the =then= and =fi= from =if=. A command block can be inside a =case=, or between a =do= and e =done=. - Hold on Julio, what are these =do= and =done= that I don't remember you had tell me about ? And look, I'm really paying attention ! - Yeah, you're right. I didn't tell it because the right time wasn't arrived. All _loop_ instructions execute the block command found between =do= e o =done=. ---++ _Loop_ Commands The _loop_ commands, or _loop_ instructions, are =for=, =while= and =until=, that I'll start to explain one by one from now. ---+++ The _for_ command If you are a programmer, or have some programming experience, I'm sure you know the =for= command. But, what you don't know is the =for=, a built-in _Shell_ instruction (that means the command's source code is part of _Shell's_ source code ), is many times more powerfull than its other languages correlated command. Lets understand its syntax, first in plain english, and then the real deal. <verbatim> for var in val1 val2 ... valn do cmd1 cmd2 cmdn done </verbatim> The variable =var= assumes any values from the list =val1 val2 ... valn= and for each one of these values, the command block made by =cmd1=, =cmd2= e =cmdn= is executed. Now we saw what that means, lets see the correct syntax: ---++++ _for_ command first syntax <verbatim> for var in val1 val2 ... valn do cmd1 cmd2 cmdn done </verbatim> That is amazing ! There is no changes between the real life, our real language, and the =for= command syntax. Very easy to learn, then. Lets right to the samples, to correctly understand how the command works. Lets make a _script_ to list all files in our directory separeted by colons, but first, see: %TERMINAL_INI% $ echo *%OUT_INI% <nop>ArqDoDOS.txt1 confuso incusu logado musexc musicas musinc muslist%OUT_FIM% %TERMINAL_FIM% That means, _Shell_ saw the star (=*=) and expanded it whit the name os all files in the directory and the =echo= command sent them to screen separated by spaces. That understood, lets see how can we solve the proposed problem: %TERMINAL_INI% $ cat testefor1%OUT_INI% #!/bin/bash # 1st. Program to understand for for Arq in * do echo -n $Arq: # The -n option doesn't generate a new line done%OUT_FIM% %TERMINAL_FIM% Then lets run it: %TERMINAL_INI% $ testefor1%OUT_INI% <nop>ArqDoDOS.txt1:confuso:incusu:logado:musexc:musicas:musinc:muslist:%OUT_FIM%$ %TERMINAL_FIM% As you can see, _Shell_ changes the star in a space-separeted list. When =for= saw that list, he said: "Yeah, space-separated lists... that's my groovy, baby !" The command block to be executed was only the =echo=, with its =-n= option, listed the variable =$Arq= followed by colons (=:=), without a new-line character. The dolar (=$=) at the end of execution line is the _prompt_. that remained at the same line also because of the =-n= echo's option. Another simple example (by now): %TERMINAL_INI% $ cat testefor2%OUT_INI% #!/bin/bash # 2nd. program to understand for for WORD in We are in the Pub Talk do echo $WORD done%OUT_FIM% %TERMINAL_FIM% Running, whe have: %TERMINAL_INI% $ testefor2%OUT_INI% We are in the Tub Talk%OUT_FIM% %TERMINAL_FIM% This is a silly simple sample, like the other one, but serves to show =for's= basic behavior. See the power of the =for=: we're still in the its first syntax and I'm showing new ways to use it. Back in our chat, I told =for= used space-separated lista, but this is not the whole truth. It was only to make understanding easy. For real, the lists are not separated by spaces unconditionally but, before go ahead, let me show you how is the behavior of a system variable called =$IFS=. Take a look at its value: %TERMINAL_INI% $ echo "$IFS" | od -h%OUT_INI% 0000000 0920 0a0a 0000004%OUT_FIM% %TERMINAL_FIM% I sent the variable (protected from _Shell_ execution by quotation marks) to a hexadecimal _dump_ (=od -h=) and I have: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *$IFS Variables Value* || | *Hexa* | *Means* | | =09= | =<TAB>= | | =20= | =<ESPACE>= | | =0a= | =<ENTER>= | </center> Where the last =0a= came from the final =<ENTER>= at the command. To maximize the explanation, lets see the other way: %TERMINAL_INI% $ echo ":$IFS:" | cat -vet%OUT_INI% : ^I$ :$%OUT_FIM% %TERMINAL_FIM% Payt attention to the following tip to understand that cat construction: %DICA_INI% At the =cat= command, option =-e= represents the =<ENTER>= whit a dolar (=$=) and the option =-t= represents the =<TAB>= as a =^I=. I used colons (=:=) to show =echo='s start and ending. This way, once again we can see that the variable =$IFS= has three characters. %DICA_FIM% Now see, =IFS= means __Inter Field Separator__. Knowing this, I can prove that =for= command doesn't use space-separated lists, but lists separated by the =$IFS= value, which _default_ are these characters we saw. To prove this, lets show a _script_ that receives the singer name as parameter and shows the musics he plays, but first lets see how our =musics= file looks like: %TERMINAL_INI% $ cat musics%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% Using this layout, we wrote the following _script_: %TERMINAL_INI% $ cat listartista%OUT_INI% #!/bin/bash if [ $# -ne 1 ] then echo You should pass a parameter 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% The _script_, as always, starts testing if the parameters was correctly passed . After that, the =IFS= was setted to =<ENTER>= and colon (=:=) (as we can see in the quotes in different lines), because it is the responsible to separate the blocks =Artistan~Musicam=. In this way, the variable =$ArtMus= will receive each one of these blocks in the file ( notice that =for= receives the records without the album because of =cut= in its line). If it found the first parameter (=$1=) at the block, the second o segundo =cut= will list only the music name. Lets run it: %TERMINAL_INI% $ listartista Artista1%OUT_INI% Artista1~Musica1 Musica1 Artista1~Musica3 Musica3 Artista10~Musica10 Musica10%OUT_FIM% %TERMINAL_FIM% Ups! Two undesireable things happen: the blocks and the =Musica10 = were listed too. Besides, our music file is quite simple. In the real life, the music and the artist have more than one name. Supose the artist was a couple named Paris & Britney (I really don't like neither to think about it... I'm affraid it can be true one day...LOL). In this case the =$1= was Paris and the rest of this beautifull name would be ignored in the search. To avoid this, I should pass the artist name between quotes (="=) or change =$1= by =$@= (that means all passed parameters), that is the best solution but, in this case, I should change parameters checking and =grep=. The new check shoud be if I passed *at least* one parameter, instead of if I passed a parameter. The =grep=, this is what we get after the substituton of =$*= for its parameters: <verbatim> echo "$ArtMus" | grep Paris & Britney </verbatim> what would generate an error. The right way is: <verbatim> echo "$ArtMus" | grep -i "Paris & Britney" </verbatim> We put the =-i= option to ignore the case and the quotes was inserted to the artist name be recognized as a one array of chars. We still need to fix the list of =Artista10= error. To do this, the best way is tell to =grep= that the array of chars is at begining of =$ArtMus= (the regular expression to do this is =^=) followed by a tilde (=~=). We need to redirect the grep messages to =/dev/null= to avoid block listing. So lets see the final program: %TERMINAL_INI% $ cat listartista%OUT_INI% #!/bin/bash # Dado um artista, mostra as suas musicas # versao 2 if [ $# -eq 0 ] then echo You should pass a parameter 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% Running it, we have: %TERMINAL_INI% $ listartista Artista1%OUT_INI% Musica1 Musica3%OUT_FIM% %TERMINAL_FIM% ---++++ _for_ command second syntax <verbatim> for var do cmd1 cmd2 cmdn done </verbatim> - What ? Whitout the =in= how he knows what value assumes ? - Yeah, right ? This construction seems strange but it is quite simple. In this case, =var= will assume one by one each passed parameters. Lets make some samples to better understanding. We'll do a _script_ to receive a bunch of musics as parameters and list its authors: %TERMINAL_INI% $ cat listamusica%OUT_INI% #!/bin/bash # Receives parts of the music as parameter and # lists its players. If the parameter is a composite name # shall be passed between quotes. # ex. "Ups I did it again" "Four died in Ohio" # 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 " not found" 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% Again, we started the exercise with a parameter check. Then we did a =for= in which the variable =$Musica= will receive each one of the passed parameters, putting in =$Str= all albuns that contain the musics. Then, the other =for= search for eah block =Artista~Musica= in the records we have in =$Str= and lists each artist that plays that music. Lets run to see if it works: %TERMINAL_INI% $ listamusica musica3 Musica4 "Poison"%OUT_INI% musica3 Artista3 Artista1 Musica4 Artista4 Poison Não encontrada%OUT_FIM% %TERMINAL_FIM% That's an ugly output because we still don't know how to format outputs, but any given day, when you know how to work with cursos positioning, bold, color, etc, we'll do this list again using all these "little stuff". At this time, we must asking: "What about that traditional, other languages, =for= that counts from a number with an addition until reachs a condition ?" I will answer you: "I told you, homeboy, our =for= is much more coolio that the others ?" There is two ways: 1 - With our first syntax, as the following examples, run straigh at the _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% In this one, the variable =i= assumed the integers 1 to 9 generated by the =seq= command and the =echo's= =-n= option was used to avoid line break after each number. Still using =for= with =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% Or with the more complete =seq= form: %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 - The other way is to do the same with a C-like syntax, showed as follow. ---++++ _for_ command third syntax <verbatim> for ((var=ini; cond; incr)) do cmd1 cmd2 cmdn done </verbatim> Onde: =var=ini= - means the variable =var= starts with an initial value _ini_ =cond= - Means that the =for= _loop_ will be executed while =var= doesn't reach the condition =cond= =incr= - Means the addition variable =var= will have in each _loop_ iteration. Examples, examples, examples, to clarify the things: %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% In this case the variable =i= started from =1=; the command block (the =echo=) will be executed while the variable =i= is less or equal than (=<==) =9= and the =i= increment will be =1= in each _loop_ iteration. Notice that at the =for= command I didn't put a dolar (=$=) before =i=, and the increment notation (=i++=) is different from what we came seeing until now. This is possible because the double parentesis (or the =let= command) calls the shell arithmetic interpreter, that is more tolerant. And, as I told about the =let= command, just to illustrate how it works and how versatile the =for= command can be, lets do the same, but with the last =for= part ommited, sending it to the command block. %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% The increment was tooked off the =for= body and sent to command block. When I used the =let=, I even need to initialize the =$i= variable. See the following commands, in the _prompt_: %TERMINAL_INI% $ echo $j $ let j++ $ echo $j%OUT_INI% 1%OUT_FIM% %TERMINAL_FIM% That means, the variable =$j= even exist and at the first =let= it assumed the =zero= value to, after the increment, get the value =1=. See how simple the things can be: %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% - That's it mate. I'm pretty sure you are full of =for= command. That's enought for today. In the next time we talk about another _loop_ instructions. I wish you to do a little script to count the words in a text file, passing its name as parameter. This counting must be done using =for= command to know it. You can't use =wc -w=. - Hey Chico! Bring me the last one.
E
dit
|
A
ttach
|
P
rint version
|
H
istory
: r3
<
r2
<
r1
|
B
acklinks
|
V
iew topic
|
M
ore topic actions
Topic revision: r3 - 23 May 2008 - 13:17:40 -
DiogoFernandes
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