You are here:
Wiki-SL
>
TWikiBar Web
>
TWikiBarConversa005
(04 Feb 2008,
CollonsTorre
)
(raw view)
E
dit
A
ttach
---+!!Conversación de bar Parte V %TOC% - Que hay amigo! Ordenaste ya tus ideas?, Se fundió ya tu cabeza, o todavía aguanta más _Shell_? - Aguanto, claro! Me esta gustando mucho! Me gustó tanto que hasta me esmeré en el ejercicio que me dejaste. Te acuerdas que me pediste que hiciera un programa que recibiría como parámetro el nombre de un archivo y que cuando se ejecutara salvara este archivo con el nombre original seguido de una tilde (=~=) ademas de colocarlo dentro del =vi=? - Claro que me acuerdo, muéstramelo y explica como lo hiciste. %TERMINAL_INI% $ cat vira%OUT_INI% #!/bin/bash # # vira - vi grabando el archivo anterior # == = = # Verificando si fue pasado 1 parámetro if [ "$#" -ne 1 ] then echo "Erro -> Uso: $0 <archivo>" exit 1 fi Arq=$1 # En caso de que el archivo no exista, no hay copia para grabar if [ ! -f "$Arq" ] then vi $Arq exit 0 fi # Si no puedo alterar el archivo, para que voy a usar el vi? if [ ! -w "$Arq" ] then echo "Usted no tiene privilegios de grabación en $Arq" exit 2 fi # Ya que está todo OK, voy a salvar la copia y llamar el vi cp -f $Arq $Arq~ vi $Arq exit 0%OUT_FIM% %TERMINAL_FIM% - Bárbaro, muy bien! Pero dime una cosa: porque terminaste el programa con un =exit 0=? - Ahhh! Descubri que el número después del =exit= da el código de retorno del programa (o =$?=, te acuerdas?), y de esta forma, si todo se ejecuto bien, se cerraría con el =$? = 0=. Sin embargo, si observas, verás que en el caso de que el programa no reciba el nombre del archivo o en el caso de que el operador no tenga privilegios de grabación sobre este archivo, el código de retorno (=$?=) sería diferente de cero. - Grande!, aprendiste bien, pero es bueno dejar claro que el =exit 0=, simplemente =exit=, o no colocar =exit=, producen igualmente un código de retorno (=$?=) igual a cero, si el programa fue bien ejecutado. Ahora vamos a hablar sobre las instrucciones de _loop_ o lazo, pero antes voy a pasar el concepto de bloque de programa. Hasta ahora ya vimos algunos bloques de programa, como cuando te mostré un ejemplo para hacer un =cd= hacia dentro de un directorio, y que era así: <verbatim> cd lmb 2> /dev/null || { mkdir lmb cd lmb } </verbatim> El fragmento contenido entre las dos llaves (={}=), forma un bloque de comandos. También en este ejercicio que acabamos de ver, en que salvamos el archivo antes de editarlo, existen varios bloque de comandos comprendidos entre los =then= y los =fi= del =if=. Un bloque de comandos también puede estar dentro de un =case=, o entre un =do= y un =done=. - Espera ahí Julio, que =do= y =done= son esos, no me acuerdo de que hayas hablado de ellos y mira que estoy prestando mucha atención... - Claro, todavía no había hablado de ellos porque no había llegado el momento adecuado. Todas las instrucciones de _loop_ o lazo, ejecutan los comandos del bloque comprendido entre el =do= y el =done=. ---++ Comandos de _Loop_ (o lazo) Las instrucciones de _loop_ o lazo son el =for=, el =while= y el =until= que pasaré a explicarte una a una a partir de hoy. ---+++ El comando for Si ya estás habituado a programar, con seguridad conoces el comando =for=, pero lo que no sabes es que el =for=, que es una instrucción intrínseca del _Shell_ (esto significa que el código fuente del comando es parte del código fuente del _Shell_, o sea en buen idioma "programés" es un _built-in_), es mucho más poderoso que los semejantes de otras lenguajes. Vamos a entender su sintaxis, primero en español y después como funciona realmente. <verbatim> para var en val1 val2 ... valn haga cmd1 cmd2 cmdn hecho </verbatim> Donde la variable =var= asume cada uno de los valores de la lista =val1 val2 ... valn= y para cada uno de esos valores ejecuta el bloque de comandos formado por =cmd1=, =cmd2= y =cmdn= Ahora que ya vimos el significado de la instrucción en español, veamos la sintaxis correcta: ---++++ Primera sintaxis del comando for: <verbatim> for var in val1 val2 ... valn do cmd1 cmd2 cmdn done </verbatim> Vamos directo a los ejemplos, a fin de entender el funcionamiento de este comando. Vamos a escribir un _script_ para listar todos los archivos de nuestro directorio separados por dos puntos, pero mira primero: %TERMINAL_INI% $ echo *%OUT_INI% <nop>ArchDoDOS.txt1 confuso incusu logado musexc musicas musinc muslist%OUT_FIM% %TERMINAL_FIM% O sea, el _Shell_ vio el asterisco (=*=), lo expandió con el nombre de todos los archivos del directorio y el comando =echo= los mostró en la pantalla separados por espacios en blanco. Visto esto vamos a ver como resolver el problema que nos propusimos: %TERMINAL_INI% $ cat testefor1%OUT_INI% #!/bin/bash # 1o. Prog didáctico para entender el for for Arch in * do echo -n $Arq: # La opción -n es para no saltar la línea done%OUT_FIM% %TERMINAL_FIM% Ahora vamos a ejecutarlo: %TERMINAL_INI% $ testefor1%OUT_INI% <nop>ArchDoDOS.txt1:confuso:incusu:logado:musexc:musicas:musinc:muslist:%OUT_FIM%$ %TERMINAL_FIM% Como viste, el _Shell_ transformó el asterisco (es odioso ser llamado por un asterisco) en una lista de archivos separados por espacios en blanco. cuando el =for= vio aquella lista, se dijo: "Opa!, lista separadas por espacios es mi especialidad!" El bloque de comandos para ejecutar era solamente el =echo=, que con la opción =-n= listó la variable =$Arch= seguida de dos puntos (=:=), sin saltar de línea. El signo de (=$=) del final de la línea de ejecución es el _prompt_. que permaneció en la misma línea también en función de la opción =-n=. Otro ejemplo simple (por ahora): %TERMINAL_INI% $ cat testefor2%OUT_INI% #!/bin/bash # 2o. Prog didáctico para entender el for for Palabra in Conversa de Bar do echo $Palabra done%OUT_FIM% %TERMINAL_FIM% Y ejecutando resulta: %TERMINAL_INI% $ testefor2%OUT_INI% Conversa de Bar%OUT_FIM% %TERMINAL_FIM% Como viste, este ejemplo es tan bobo y simple como el anterior, pero sirve para mostrar el comportamiento básico del =for=. Fíjate en la fuerza del =for=: todavía estamos en la primera sintaxis del comando y ya estoy mostrando nuevas formas de usarlo. Allá atrás, te había hablado que el =for= usaba listas separadas por espacios en blanco, pero eso es una verdad a medias, era sólo para facilitar la compresión. En realidad, las listas no son obligatoriamente separadas por espacios, pero antes de seguir, déjame mostrarte como se comporta una variable del sistema llamada =$IFS=. Observa su contenido: %TERMINAL_INI% $ echo "$IFS" | od -h%OUT_INI% 0000000 0920 0a0a 0000004%OUT_FIM% %TERMINAL_FIM% O sea, mandé la variable (protegida de la interpretación del _Shell_ por las comillas) para un _dump_ hexadecimal (=od -h=) y resultó: <center> %TABLE{ databg="#ffffff" headerrows="1" }% | *Contenido de la Variable $IFS* || | *Hexadecimal* | *Significado* | | =09= | =<TAB>= | | =20= | =<ESPACIO>= | | =0a= | =<ENTER>= | </center> Donde el último =0a= fue originado por el =<ENTER>= dado al final del comando. Para mejorar la explicación, vamos a ver eso de otra forma: %TERMINAL_INI% $ echo ":$IFS:" | cat -vet%OUT_INI% : ^I$ :$%OUT_FIM% %TERMINAL_FIM% Presta atención a lo siguiente para entender la construcción del comando cat: %DICA_INI% En el comando =cat=, la opción =-e= representa el =<ENTER>= como un signo de pesos (=$=) y la opción =-t= representa el =<TAB>= como un =^I=. Usé los dos puntos (=:=) para mostrar el inicio y el fin del =echo=. y de esta forma, otra vez podemos notar que los tres caracteres están presentes en aquella variable. %DICA_FIM% Ahora, =IFS= significa __Inter Field Separator__ o, traduciendo, separador entre campos. Una vez entendido eso, puedo afirmar (porque lo voy a probar) que el comando =for= no usa listas separadas por espacios en blanco, sino por el contenido de la variable =$IFS=, cuyo valor por defecto (_default_) son esos caracteres que acabamos de ver. Para comprobarlo, vamos a mostrar un _script_ que recibe el nombre del artista como parámetro y lista las músicas que este ejecuta, pero primero veremos como está nuestro archivo =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% En base a este esquema mostrado arriba fue desarrollado el _script_ que sigue: %TERMINAL_INI% $ cat listartista%OUT_INI% #!/bin/bash # Dado un artista, muestra sus músicas if [ $# -ne 1 ] then echo Usted debería haber pasado un parámetro 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% El _script_, como siempre, comienza testando si los parámetros fueron pasados correctamente, en seguida el =IFS= fue configurado para =<ENTER>= y dos puntos (=:=) (como demuestran las comillas en líneas diferentes), porque es él el que separa los bloques =Artistan~Musicam=. De esta forma, la variable =$ArtMus= irá a recibir cada uno de estos bloques del archivo (observa que el =for= ya recibe los registros sin el álbum en virtud del =cut= en su línea). En el caso de que encuentre el parámetro (=$1=) en el bloque, el segundo =cut= listará solamente el nombre de la música. Vamos a ejecutarlo: %TERMINAL_INI% $ listartista Artista1%OUT_INI% Artista1~Musica1 Musica1 Artista1~Musica3 Musica3 Artista10~Musica10 Musica10%OUT_FIM% %TERMINAL_FIM% Epa! Pasaron dos cosas no deseadas: los bloques también fueron listados y la =Musica10= también. Además de eso, nuestro archivo de músicas es muy simple, en la vida real, tanto la música como el artista tienen más de un nombre. Suponte que el artista fuera una dupla de música folclórica llamada _Clitandro & Eduviges_ (no me atrevo ni a dar la idea, por miedo a que se haga realidad :) ). En este caso el =$1= sería Clitandro y el resto de este lindo nombre sería ignorado en la búsqueda. Para que eso no ocurriese, debería pasar el nombre del artista entre comillas (="=) o alterar =$1= por =$@= (que significa todos los parámetros pasados), que es la mejor solución, pero en este caso tendría que modificar la crítica de los parámetros y el =grep=. La nueva crítica no actuaria si yo pasase un parámetro, o *por lo menos* un parámetro y en cuanto al =grep=, mira lo que resultaría después de la substitución del =$*= (que entraría en lugar del =$1=) por los parámetros: <verbatim> echo "$ArtMus" | grep clitandro & eduviges </verbatim> Lo que resultaría en un error. Lo corretco sería: <verbatim> echo "$ArtMus" | grep -i "clitandro & eduviges" </verbatim> Donde fue colocada la opción =-i= para que la búsqueda ignorase mayúsculas y minúsculas y las comillas también fueron insertadas para que el nombre del artista fuera visto como una cadena única y monolítica. Todavia falta arreglar el error de haber listado al =Artista10=. Para esto, lo mejor es decirle al =grep= que la cadena está en el início ( forma cuya expresión regular es =^=) de =$ArtMus= y luego después viene una tilde (=~=). Es necesario también que se redireccione la salida del grep para =/dev/null= para que los bloques no sean listados más . Observa entonces la nueva (y definitiva) cara del programa: %TERMINAL_INI% $ cat listartista%OUT_INI% #!/bin/bash # Dado un artista, muestra sus músicas # versao 2 if [ $# -eq 0 ] then echo Usted debería haber pasado un parámetro 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 ejecutado da: %TERMINAL_INI% $ listartista Artista1%OUT_INI% Musica1 Musica3%OUT_FIM% %TERMINAL_FIM% ---++++ Segunda sintáxis del comando for: <verbatim> for var do cmd1 cmd2 cmdn done </verbatim> - Espera ahí!, sin el =in= como va a saber que valor asumir? - Eso mismo, no? Esta construcción a primera vista parece extraña pero es bastante simple. En este caso, =var= asumirá uno a uno cada uno de los parámetros pasados para el progama. Vamos rapidito a los ejemplos para entenderlo mejor. Vamos a hacer un _script_ que reciba como parámetro una cantidad de músicas y liste sus autores: %TERMINAL_INI% $ cat listamusica%OUT_INI% #!/bin/bash # Recibe parte de los nombres de músicas como parámetro y # lista los intérpretes. Si el nombre es compuesto, debe # ser pasado entre comillas. # ex. "No soy tu perrito, no!" "Asadito de Madre" # 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 " No 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% De la misma forma que los otros, comenzamos el ejercício con una crítica sobre los parámetros recibidos, en seguida hicimos un =for= en que la varible =$Musica= recibirá cada uno de los parámetros pasados, colocando en =$Str= todos los álbums que contienen las músicas pasadas. En seguida, el otro =for= coge cada bloque =Artista~Musica= de los registros que están en =$Str= y lista cada artista que ejecute aquella música. Como siempre vamos a ejecutarlo para ver si realmente funciona: %TERMINAL_INI% $ listamusica musica3 Musica4 "Yegüita Pocotó"%OUT_INI% musica3 Artista3 Artista1 Musica4 Artista4 Yegüita Pocotó No encontrada%OUT_FIM% %TERMINAL_FIM% La lista quedó fea porque todavia no sabemos dar formato a la salida, pero cualquier día de estos, cuando sepas posicionar el cursor, hacer negritas, trabajar con colores, etc, haremos esta lista nuevamente usando todas estas perfumerías y entoces quedará bien coqueto. A esta altura de los acontecimientos debes estar preguntandote: "Y aquél =for= tradicional de los otros lenguajes en que sale contando a partir de un número, con un determinado incremento hasta alcanzar una condición?" Y es ahí donde te respondo: "Yo no te dije que nuestro =for= es más completo que los otros?" Para hacer eso existen dos formas: 1 - con la primera sintáxis que vimos, como en los siguientes ejemplos directamente en el _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% Aquí, la variable =i= asumió los enteros del 1 al 9 generados por el comando =seq= y la opción =-n= del =echo= fue usada para no saltar de línea con cada número listado (me siento ecologicamente correcto por no gastar una cantidad de papel de la revista cuando eso puede ser evitado). Además usando el =for= con =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% O todavia en la forma más completa del =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 – La otra forma de hacer lo deseado es con una sintáxis muy parecida al =for= del lenguaje C, como veremos a continuación. ---++++ Tercera sintáxis del comando for: <verbatim> for ((var=ini; cond; incr)) do cmd1 cmd2 cmdn done </verbatim> Donde: =var=ini= - Significa que la variable =var= comenzará a partir de un valor inicial _ini_;%BR% =cond = - Significa que el _loop_ o lazo del =for= será ejecutado en cuanto la =var= no cumpla la condición =cond=;%BR% =incr = - Significa el incremento que la variable =var= sufrirá en cada pasada del _loop_. Como siempre vamos a los ejemplos y la cosa quedara más 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% En este caso la variable =i= partió del valor inicial =1=, el bloque de comando (aqui solamente el =echo=) será ejecutado en cuanto =i= sea menor o igual (=<==) a =9= y el incremento de =i= será de =1= a cada pasada del _loop_. Fíjate que en el =for= propiamente dicho (y no en el bloque de comandos) no coloqué un signo de pesos (=$=) antes del =i=, y la notación para incrementar (=i++=) es diferente de la que vimos hasta ahora. Esto es porque el uso de paréntesis dobles (así como el comando =let=) llama el interpretador aritmético del _Shell_, que es más tolerante. Como me referí al comando =let=, y sólo para mostrar como funciona, vamos hacer lo mismo, omitiendo sin embargo, la última parte del =for=, pasándola hacia el bloque de comandos, así ademas veras la versatilidad del =for=. %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% Observa que el incremento desapareció del cuerpo del for y pasó dentro del bloque de comandos, fíjate también que cuando usé el =let=, no fue necesario siquiera inicializar la varible =$i=. Observa los siguientes comandos escritos directamente en el_prompt_ para mostrar lo que acabo de decir: %TERMINAL_INI% $ echo $j $ let j++ $ echo $j%OUT_INI% 1%OUT_FIM% %TERMINAL_FIM% O sea, la variable =$j= ni siquiera existía y en el primero =let= asumió el valor =0= (cero) para, después del incremento, tener el valor =1=. Fíjate en lo simples que son las cosas: %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% - Y hasta aqui amigo!, tengo la seguridad que hoy tomaste una buena dosis de jarabe del comando =for=. Por hoy es suficiente, la próxima vez que nos encontremos hablaremos sobre otras instruciones de _loop_, pero me gustaria que hasta entonces, hicieses un pequeño _script_ para contar la cantidad de palabras de un archivo texto, cuyo nombre sería recibido por parámetro. OBS: Esa cuenta tiene que ser hecha usando el comando for para que te habitues a su uso. No vale usar o =wc -w=. - Chico! Tráeme, por favor la del estribo! Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar mal de los políticos lo único que tienes que hacer es mandarme un e-mail para <a href="mailto:julio.neves@gmail.com?Subject=Dudas Conversas de bar botequim">julio.neves@gmail.com</a>. Voy aprovechar también para mandar mi aviso publicitario: puedes decirle a los amigos que quien quiera hacer un curso nota diez de programación en _Shell_ que mande un e-mail para <a href="mailto:julio.neves@uniriotec.br?Subject=Curso de Shell con Julio Neves">julio.neves@uniriotec.br</a> para informarse. Gracias y hasta la <a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarConversa006">próxima</a> -- Main.HumbertoPina - 20 Oct 2006
E
dit
|
A
ttach
|
P
rint version
|
H
istory
: r5
<
r4
<
r3
<
r2
<
r1
|
B
acklinks
|
V
iew topic
|
M
ore topic actions
Topic revision: r5 - 04 Feb 2008 - 22:37:56 -
CollonsTorre
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