Uso y programación de bash, Parte III (final).

Uso y programación de bash, Parte III (final).

21. Miscelánea.

22. Dialogando con bash.

23. Los comandos grep, sort y tr.

24. El comando mawk.

25. Ejemplo: Un robot IRC.

26. Bibliografía y despedida.

21. Miscelánea.

En esta sección veremos algunos temas cortos que complementan
puntos anteriores.

21.1.Obtencion del valor de una variable.
Recordemos que para obtener el valor de una variable, le anteponemos
el signo \"$\", por ejemplo:

$ echo $VAR

Esto es equivalente a: ${VAR}

$ echo ${VAR}

La diferencia es que esta ultima forma nos permite concatenar el
valor de la variable con otra cadena de caracteres, por ejemplo:

$ VAR=foto
echo ${VAR}.jpg
foto.jpg
$ echo ${VAR}.gif
foto.gif
$ echo ${VAR}.xpm
foto.xpm

En cambio, en el primer caso si hacemos $VAR.gif, el bash
interpretara toda la cadena VAR.gif como si fuera el nombre de
la variable y por lo tanto no hará lo esperado.

21.2.Valor por defecto de una variable.
El valor por defecto de una variable, ósea el valor que posee
antes de asignarle un valor es una cadena nula (\"\").

$ echo VAR2
$

Podemos modificar el valor por defecto de una variable, de la siguiente
manera: {VAR:-Valor}, su valor por defecto ahora será Valor.

$ echo $VAR2
$ echo ${VAR2:-Hola}
Hola
$

El valor por defecto también puede ser un comando, por ejemplo:
${VAR2:-`pwd`}.

21.3.Mas sobre redireccionamiento.
En el punto 4 vimos como realizar redireccionamiento desde o hacia
un archivo e interconexión entre procesos, ahora veremos algunos
detalles mas.

Cuando un programa quiere escribir o leer un archivo, lo primero
que hace es solicitarle al sistema operativo su apertura, si la solicitud
tiene éxito, el sistema le devuelve un descriptor de archivo, que
es el identificador que el programa utilizara para escribir y/o leer dicho
archivo, este descriptor es un numero entero.

No solo los archivos se identifican mediante descriptores de
archivos, la mayoría de los dispositivos operan de la misma manera,
el programa los trata como si fueran archivos.

Cuando comienza la ejecución de cada programa, el sistema
le entrega 3 descriptores, que identifican 3 archivos estándar,
la entrada estándar, la salida estándar y el error estándar
(donde el programa envía los mensajes de error).

La entrada estándar se identifica con el descritpor de
archivo numero 0, la salida estándar con el 1 y el error estándar
con el 2. Lo mas normal es que la entrada estándar sea el teclado
y que la salida y error estándar sean la pantalla.

Cuando redireccionamos la salida de un comando a un archivo, de la siguiente
manera:

$ comando > archivo

Lo que hace el sistema operativo es asociar el descriptor 1 con el archivo,
entonces toda la salida que el programa realice al descriptor 1 (salida
estándar) se dirigirá al archivo.

En forma análoga funciona el redireccionamiento de la entrada
estándar.

$ comando < archivo

Toda las lecturas que el programa realiza de la entrada estándar,
son tomadas del archivo.

También podemos realizar redireccionamiento entre descriptores,
por ejemplo queremos guardar el error que da un programa, intentamos de
la siguiente manera:

$ programa > error.out

Pero luego leemos el archivo error.out y no contiene el error.
Que paso ? Eso sucede porque lo que redireccionamos al archivo fue la salida
estándar y el programa escribió el mensaje de error al error
estándar, entonces tenemos que hacerlo así :

$ programa 2> error.out

Ahora si efectivamente redireccionamos el error estándar a un archivo.

Ahora supongamos un ejemplo donde queremos guardar lo que el programa
escribe a la salida estándar y error estándar, ambas juntas
a un archivo. Lo que podemos hacer es añadir el error estándar
a la salida estándar y redireccionar la salida estándar a
un archivo:

$ comando 2>&1 > archivo

Mediante el 2>&1 añadimos lo que el programa escribe
al descriptor 2 (error estándar) al descriptor 1 (salida estándar),
y luego redireccionamos la salida estándar a un archivo, obteniendo
lo deseado.

Veamos en resumen lo que vimos:



m > archivo

Redirige lo que el programa escribe al descriptor m a un archivo, si
el archivo contenía datos, se pierden.

m >> archivo

Agrega al final del archivo, lo que el programa escriba al descriptor
m

m > &n

Añade lo que el programa escriba al descriptor m, al descriptor
n.

22. Dialogando con bash.

Veremos como se utiliza el programa dialog, para crear ventanas
en la consola desde un script bash, de esta manera nuestro script se vera
mucho mas profesional e interactivo.

su sintaxis es:

dialog [] 

Veamos primero las opciones_ventana:
--infobox texto largo ancho

Por lo general se utiliza para notificar sobre una acción que
se esta realizando.

texto es el mensaje que aparece en la ventana.

largo y ancho son el tamaño vertical y horizontal
de la ventana respectivamente, si se especifican como \"0\" (cero), dialog
le dará los valores apropiados automáticamente. Recordemos
que el tamaño estándar de una terminal es un ancho de 80
y un largo de 25.

Ejemplo:

$ dialog --infobox \"Realizando copia de archivos...\" 0 0


--yesno texto largo ancho

Se utiliza para solicitar una confirmación de la tarea que se
realizara, texto, largo y ancho significan lo mismo que en
el caso anterior.

$ dialog --yesno \"I\'m too lame ?\" 0 0

Retornara un valor cero, si se selecciono YES o un valor distinto de
cero si se selecciono NO, como vimos anteriormente, este valor se almacena
en la variable $? y puede ser evaluado mediante un if, while, etc.

#!/bin/bash
# Cualquier coincidencia con la realidad es pura casualidad ;-)
if dialog --yesno \"Se ha producido el error XXXX, desea continuar ?\" 0 0
 then
 dialog --infobox \"Programa finalizado..intentelo de nuevo\" 0 0
 else
 dialog --infobox \"Programa finalizado por el usuario\" 0 0
fi

--msgbox texto largo ancho

Es igual a --yesno, excepto que solo tenemos para seleccionar la opción
\"OK\". Se utiliza para avisar una acción y asegurarse que el usuario
este enterado de lo que va a suceder.

$ dialog --msgbox \"Esta operación durara unos minutos\" 0 0

--gauge texto largo ancho porcentaje.

Crea un medidor de avance de una tarea, porcentaje es el valor
inicial del porcentaje de la tarea realizada, por lo general es cero.

Para actualizar el valor del medidor, dialog lee el porcentaje desde
su entrada estándar.

En este caso hay que indicarle los valores largo y ancho
porque sino no funciona correctamente.

$ for var in 0 10 20 30 40 50 60 70 80 90 100; \\
do sleep 1; echo $var; done | dialog --gauge \"Total completado\" 5 40 0

Nota: Las barra invertida (\\) al final de la linea se utiliza para continuar
el comando en la linea inferior.

--inputbox texto largo ancho [valor_inicial]

Solicita el ingreso de una linea de texto al usuario,
valor_inicial
es opcional y es el valor que se ingresara si el usuario no escribe y solamente
presiona \"OK\"

Ejemplo:

$ dialog --inputbox \"Ingrese su nombre\" 0 0 \"Pepe\"

Retornara un valor cero, si se selecciono OK o un valor distinto de
cero si se selecciono CANCEL, este valor se almacena en la variable $?
y puede ser evaluado mediante un if, while, etc.

--passwordbox texto largo ancho

Es similar a inputbox, excepto que no se muestra el texto que
ingresa el usuario.

--tailbox archivo largo ancho

Muestra el final de un archivo, como lo hace el comando \"tail -f\"

$ dialog --tailbox /etc/passwd 20 60

--textbox archivo largo ancho

Muestra el texto del archivo igual que un editor, se puede desplazar
a través del texto con las teclas UP/DOWN, LEFT/RIGTH, PGUP/PGDN
y HOME/END.

--menú texto largo ancho largo-lista marcador1 elemento1 marcador2
elemento2 ...

Se utiliza para presentar una lista de opciones en la forma de
un menú, cada opción del menú consiste de un marcador
y un elemento (marcador1 elemento1 marcador2 elemento2 marcador3 elemento3
...), los marcadores no se deben repetir porque es lo que distingue
una opción del menú de las demas.

El usuario se puede mover a través del menú con
las teclas UP/DOWN hasta posicionarse encima de la opción deseada
y debe pulsar sobre \"OK\" pasa seleccionarla.

largo-lista es la cantidad de opciones que se muestran
simultáneamente, si la cantidad de elementos de la lista es mayor
que largo-lista, se podrá desplazar dentro del cuadro hasta
encontrar los elementos que queden fuera de la zona visible, para
ser seleccionados.

Cuando el usuario presiona \"OK\", el marcador (marcador1, marcador2,
etc) de la opción seleccionada es enviado a la salida de error estándar.

$ dialog --menú \"Seleccione su distribución favorita\" 0 0 0 \\
RH \"Red Hat\" \\
MK Mandrake \\
SL Slackware \\
CL Conectiva \\
DB \"Debian GNU/Linux\"

Retornara un valor cero, si se selecciono OK o un valor distinto
de cero si se selecciono CANCEL, este valor se almacena en la variable
$? y puede ser evaluado mediante un if, while, etc.

--checklist texto largo ancho largo-lista
marcador1 elemento1 estado1 marcador2 elemento2
estado2 ....

Es similar a menú, excepto que existe la posibilidad de seleccionar
varias opciones simultáneamente. Cada opción posee un estado
(estado1, estado2, estado3 ...) que se encuentra en \"on\" cuando la opción
esta seleccionada o tildada, o se encuentra en \"off\" cuando la opción
esta sin seleccionar.

ancho-lista es igual que en el caso anterior.

Una vez que el usuario tildo los elementos y selecciona \"OK\"
, dialog envía a la salida de error estándar la lista de
los marcadores de los elementos tildados, por ejemplo: \"LI\" \"FREE\"
\"DOS\".

Podemos redireccionar la salida de error estandar a un archivo para
guardar las opciones tildadas.

Ejemplo:

$ dialog --checklist \"Que sistema operativo usa ?\" 0 0 4 \\
WIN \"Windows 9x\" off \\
LI Linux on \\
FRE FreeBSD on \\
DOS MS-DOS off 2> /tmp/salida.txt
$ echo /tmp/salida
\"LI\" \"FREE\"

--radiolist texto largo ancho largo-lista
marcador1 elemento1 estado1 marcador2 elemento2
estado2 ...

Es similar a un menubox, la única diferencia es que para seleccionar
un elemento, se debe establecer su estado en \"on\".

Veamos algunas de las opciones_comunes:

--backtitle texto

Especifica un texto que se muestra en la parte superior de la pantalla.

--defaultno

hace que el valor por defecto de la ventana yes/no sea NO.

--no-shadow

Suprime el sombreado en las ventanas.

--sleep segundos

detiene la ejecución el numero de segundos especificados, luego
de que dialog finalizado su trabajo, útil con --infobox y --gauge.

--title Titulo

Especifica un titulo que se visualiza en la parte superior de
la ventana.

Estas son algunas de las opciones_comunes que se pueden aplicar
a cualquiera ventana, para mas opciones leer la pagina del manual de dialog
(man dialog).

Existen varios programas compatibles con dialog para X-Window
o consola. La elección depende del gusto de cada uno.

Algunos programas compatibles con dialog, pero crean ventanas
gráficas en X-Window:

xdialog - muestra ventanas en cualquier window manager o desktop
manager.

kdialog - versión para KDE.

Compatibles con dialog, pero crean ventanas en consola:

whiptail - Muestra ventanas en consola, igual que dialog, pero
con un aspecto ligeramente distinto (creo que es la que utiliza Red Hat
en la instalación modo consola).

23.Los comandos grep, sort y tr.

Veremos el uso de algunos programas utilices para operar sobre un flujo
de datos, ósea lee datos, le realiza alguna modificación
y los entrega.

23.1 El comando grep.
Su sintaxis es:

grep [opciones] patrón [archivo1 archivo2 ...]

Examina los archivos de la linea de comando (archivo1, archivo2,
etc) o la entrada estándar si estos se omiten en busca de ocurrencias
del patrón en cada linea, si encuentra una o mas ocurrencias
en una linea, imprime dicha linea en la salida estándar.

Algunas opciones útiles:

-i

Ignora el uso de mayúsculas y minúsculas.

-n

Imprime el numero de linea.

-v

Invierte el sentido de prueba, ósea se mostraran todas las
lineas que no tengan una ocurrencia del patrón.

patrón es una \"expresión regular\", ósea
un cadena de caracteres donde algunos poseen un significado especial,
algunos de los caracteres especiales de una \"expresión regular\"
tienen significado distinto al que le da el bash.


El patrón debe estar entre apóstrofos (\'\'),
para que el bash no los interprete, porque algunos son los mismos caracteres.

Metacaracteres de una expresión
regular.


^

El patrón debe estar al inicio de la linea.

Ej: grep \'^Hola\' file - Imprime a pantalla las linea
de file que comiencen con \"Hola\"

$

El patrón debe estar al final de la linea.

Ej: grep \'Hola$\' file - Imprime las lineas de file
que finalicen con \"Hola\"

[...]

Cualquier carácter que se encuentre dentro del rango, por ejemplo
[a-z] significa cualquier letra minúscula.

Ej: grep \'[123]0\' file - Imprime a pantalla las lineas de
file
que contengan los números 10, 20 o 30.

[^...]

Cualquier carácter que no este dentro del rango, por
ej [^abc] significa que no sea las letras a, b, o c.

Ej: grep \'[^123]0\' file - Imprime a pantalla las lineas de
file
que contengan un carácter distinto de 1, 2 y 3 seguido por 0.

.

Equivalente al carácter \"?\" del bash. Concuerda con único
carácter.

Ej: ls /dev | grep \'hd.1$\' - Imprime a pantalla hda1 hdb1
hdc1 hdd1 etc.

*

El \"*\" se aplica sobre otros caracteres o metacaracteres
y significa \"cero o mas ocurrencias\". Por ejemplo \'x*\'
concuerda con cero o mas \"x\" sucesivas. La cadena \".*\"
es el equivalente al \"*\" en el bash, concuerda con cualquier
cadena.

Ejemplo: grep \'a[a-zA-Z]*b\' file - Imprime a pantalla las lineas
de file que contengan una cadena formada por: una \"a\" seguida de
cero o mas letras y luego una \"b\".

\\

Se utiliza para quitarle el significado especial que poseen los metacaracteres.

Ej: grep \'\\^\' file - Imprime a pantalla las lineas de file
que contengan el carácter \"^\".

Existen dos programas similares a grep, se llaman fgrep y
egrep.

egrep interpreta expresiones regulares extendidas y permite realizar
operaciones AND y OR entre expresiones regulares.

fgrep busca muchas cadenas literales simultáneamente,
no les da un significado especial a los metacaracteres, solo busca las
cadenas que se le indico separadas por un carácter nueva linea.

Solo vimos una introducción a estos comandos, en la pagina del
manual de grep se puede leer mas detalles.

23.2. El comando sort.
Realiza ordenamiento de lineas de texto, su sintaxis es:

sort [opciones] [archivo1 archivo2 ....]

Escribe la concatenación de las lineas de textos ordenadas
a la salida estándar. El texto a ordenar lo puede leer desde los
archivos indicados en la linea de comando, o si estos se omiten, lee desde
la entrada estándar.

Algunas opciones:

-f

Son equivalente las mayúsculas y minúsculas en
el ordenamiento.

-d

Considera solo las letras y caracteres en el ordenamiento.

-n

Ordena según el valor numérico, útil para
ordenamiento de números

-r

Invierte el sentido de ordenamiento.

-k POS1[,POS2]

Divide a cada linea en campos y utiliza para ordena desde el
campo POS1 hasta POS2. Se numeran los campos comenzando en uno.

-o archivo

Escribe el resultado a
archivo.

Para mas detalles, leer la pagina del manual de sort.

23.3 El comando tr.
El comando tr sustituye o elimina caracteres de su entrada estándar
y escribe el resultado a su salida estándar, su sintaxis es:

tr [opciones] SET1 [SET2]

Por ejemplo:

tr [a-z] [A-Z]

Convierte su entrada estándar a mayúsculas y lo escribe a
su salida estándar.

Opciones:

-d

Elimina de su entrada estándar, los caracteres que se encuentran
en SET1

-s

Reemplaza secuencia de caracteres por un solo carácter.

24. El comando mawk.

mawk es un interprete del lenguaje AWK, útil para
manipulación y procesamiento de flujo de datos, su sintaxis es la
siguiente:

mawk [-W opciones] [-F valor] [-v var=valor] \'texto-programa\' \\
[archivo1 archivo2...]


texto-programa es el programa que interpretara, debe
estar entre apóstrofos. Un programa en AWK es una secuencia de pares
patrón-acción, de la siguiente manera:

patron1 { accion1 }
patron2 { accion2 }
...

mawk lee de a una linea por vez de los archivos (archivo1,
archivo2 ...) y verifica si el patron1 concuerda con el contenido de dicha
linea, si es verdadero entonces ejecuta la accion1, lo mismo realiza con
patron2 y así sucesivamente hasta que finaliza el programa. Luego
lee la próxima linea de texto y realiza lo mismo y así sucesivamente
hasta que lee todos los archivos especificados. Si no se especifican archivos,
lee de su entrada estándar.

mawk divide a cada linea de entrada en campos, separados
por blancos, y los almacena en las variables propias llamadas: , ,
....., $NF, donde $NF es el ultimo campo de la linea.

También posee variables predefinidas, como por ejemplo:

{sp_content} - contiene toda la linea de entrada.

NF - contiene el numero de campos de la linea, no confundir con $NF
que es otra variable.

Existen otras variables predefinidas que se verán mas adelante.

No es necesario que los nombres de variables comiencen con
\"$\",
como en el bash. Solo algunas variables especiales. como las que almacenan
los campos (, ...), comienzan con \"$\".

Por ejemplo si la linea dice:

Como me gusta el awk

mawk la dividirá en campos y los estados de las variables
serán:

{sp_content}=\"Como me gusta el awk\"
=\"Como\"
=\"me\"
=\"gusta\"
=\"el\"
=\"awk\"
$NF=\"awk\"
NF=5

24.1.Tipos de datos y operadores.
Como todo lenguaje de programación, deben existir variables y operadores
matemáticos (suma resta, ...), relaciónales (mayor, igual,
...) y lógicos (and, or, ...) . Existen dos tipos de datos, numéricos
que poseen coma flotante y cadenas de caracteres que deben estar encerradas
entre comillas dobles.

contador=2
mensaje=\"Hola Mundo\\n\"

La declaración de las variables no es necesaria, se realiza automáticamente
cuando las utilizamos, sus valores iniciales serán: cero para las
variables numéricas y la cadena nula (\"\") para las
variables cadena de caracteres.

Las cadenas de caracteres pueden incluir
caracteres especiales, que se listan en siguiente tabla:



\\b

Backspace.

\\t

Tabulador.

\\n

Nueva linea.

\\v

Tabulador vertical.

\\f

Alimentación de pagina.

\\r

Retorno de carro.

\\\\

\\

\\\"

\"

\\ddd

ascii representado por el valor ddd en octal.

\\xhh

ascii representado por el valor hh en hexadecimal.

La concatenación de cadenas de caracteres se realiza escribiendo
los nombres de variables o cadenas uno a continuación de otro.

$ mawk \'BEGIN {c1=\"Hola \"; c2=\"mundo\"; c3=c1 c2; print c3}\'
Hola mundo

Operadores matemáticos.

+ -

Suma y resta.

* / %

Multiplicación, división y resto.

++ --

Incremento y decremento. Ej cont++ es igual que contador=contador+1.

Operadores relaciónales.
< >

Menor y mayor.

<= >=

Menor o igual y mayor o igual.

== !=

Igual y distinto.

~ !~

Concordancia y no concordancia (semejante a igual y distinto, pero
para expresiones regulares).

Operadores lógicos.

||

OR lógico.

&&

AND lógico.

!

Negador.

24.2.Patrones.
Los patrones (patron1, patron2, ...) están compuestos por
una o mas expresiones relaciónales, en las cuales se pueden utilizar
expresiones regulares o cadenas de caracteres.

Se pueden vincular varias expresiones relaciónales mediante
operadores lógicos. Los operadores relaciónales y lógicos
son los vistos en el punto anterior.

Las expresiones regulares contienen los mismos metacaracteres
vistos con grep.

Las cadenas deben estar encerradas entre comillas dobles (\"cadena\")
y las expresiones regulares entre barras (/expresión/).

Ejemplo de patrones que contienen una sola expresión:

 == \"Como\"
 == \"me\"
 ~ /^Como/
 ~ /me/
 < 2
NF != 5
contador == 3
OS ~ /^Linux/

Cuando se compara con una expresión regular, el signo \"==\"
se reemplaza por \"~\"

Ejemplos de patrones que contiene varias expresiones:

 == \"Como\" &&  = \"me\"
NF = 3 || NF = 5
x < 3


Ademas existen dos patrones especiales, que son las palabras:

BEGIN
END

El patrón BEGIN establece una acción que se
ejecutara al inicio del programa, se puede utilizar para establecer valores
iniciales de variables o para imprimir encabezados de tablas.

El patrón END establece una acción que
se ejecutara al final del programa, se puede utilizar para imprimir resultados
finales como sumas, promedios, cantidad de lineas de texto, etc.

Ejemplo:

Hagamos que la salida del comando who sea mas elegante, agregándole
un encabezado y una linea al final.

$ who | mawk \'
BEGIN {print \"User\\ttty\\tFecha Hora\\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\n\"}
 {print {sp_content}}
END {print \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\n\"}\'


Los \"\\t\" son tabuladores y los \"\\n\"
son caracteres de nueva linea. Su salida se vera:

User tty Fecha Hora
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
root tty1 Sep 23 22:02
ariel tty2 Sep 23 22:02
ariel ttyp1 Sep 23 23:19 (:0.0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

24.3 Acciones.
Las acciones (accion1, accion2 ...) puede ser operaciones matemáticas,
operaciones lógicas, comparaciones, sentencias de control de flujo,
llamadas a funciones, etc.

24.4.Impresion a la salida estándar:

Para escribir a la salida estándar disponemos de dos funciones
internas: print y printf.

La sintaxis de print es:

print var1, var2, var3 ...

Imprime a la salida estándar las variables, separadas por un espacio
en blanco.

print \"El promedio es: \", promedio
print(\"Se proceso \", cantidad, \" linea[s] de texto\")

Las llaves que encierran los argumentos son opcionales.

La función printf funciona igual que en el Lenguaje C,
veremos solo una introducción, su sintaxis es:

printf(\"cadena-de-control\", var1, var2, var3, ...)

La cadena de control es una cadena de caracteres que se imprime a la salida,
excepto las cadenas de control: %d, %s,
etc, que tienen un significado especial, son reemplazados por las variables
(var1, var2, ...), ósea existirá la misma cantidad de %carácter
que de variables.

%d significa que le corresponde una variable entera.

%s significa que le corresponde una cadena de caracteres.

%f significa que le corresponde un numero con coma.

Veamos como serian los dos ejemplos anteriores con printf:

printf(\"El promedio es: %d\\n\", promedio)
printf(\"Se proceso %d linea[s] de texto\\n\", cantidad)

Para mas información sobre printf consultar su pagina del manual
(man 3 printf).

Ejemplo:

Mediante who y mawk imprimimos a pantalla los campos
tty y hora para el usuario \"ariel\".

$ who | mawk \'==\"ariel\" { print , }\'
tty1 12:13
tty2 12:13
ttyp0 13:59
ttyp1 15:03

En este ejemplo, el patrón es ==\"ariel\", ósea
que si la primer columna es la cadena \"ariel\", entonces se ejecutara la
acción, la cual dice print , , significa que imprimirá
los campos 2 y 5, que son la tty y la hora respectivamente.

El patrón o la acción pueden omitirse, pero no
ambos a la vez, si se omite el patrón, significa que siempre se
realiza la acción, si se omite la acción, se imprime la linea
completa por defecto.

24.5.Sentencias de control.

if (expr) sentencia1 else sentencia2

Se evalúa la expresión expr, si es verdadera se ejecuta
la sentencia1, si es falsa se ejecuta la sentencia2.

El else con su sentencia son opcionales.

while(expr) sentencia

Se evalúa la expresión expr, si es verdadera se ejecuta
la sentencia sentencia, luego se vuelve a evaluar la misma expresión,
si sigue siendo verdadera se vuelve a ejecutar la misma sentencia, en la
primera oportunidad que la expresión expr sea falsa, se continua
la ejecución por debajo de la sentencia sentencia.

$ mawk \'BEGIN { while(a<10) print a++ }\'
0
1
2
...
do sentencia while(expr)

Similar al caso anterior, excepto que primero se ejecuta la sentencia y
luego se evalúa la expresión por primera vez.

for(sentencia1, expr1, sentencia2) sentencia

El bucle for es semejante al siguiente código:

sentencia1
while(exprl)
 {
 sentencia
 sentencia2
 }


La sentencia continue:

La ejecución de continue dentro de un bucle, produce
que la ejecución continue en la próxima iteración,
ósea se produce un salto a la evaluación de la expresión.

La sentencia break:

La ejecución de break dentro de un bucle produce una salida inmediata
del bucle, ósea se produce un salto afuera del bucle.

24.6.Arrays

Se pueden utilizar arrays de una sola dimensión, ósea
un solo indice, de la forma:

array[indice]

Los arrays de este lenguaje tienen la particularidad que el indice
es una cadena de caracteres, por eso se llaman arrays asociativos.

No es necesario definir los arrays ni tampoco su tamaño,
al usar una variable como array se define automáticamente.

Existe un bucle for especial para poder leer los valores de los
arrays asociativos.

for (var in array) sentencia

La variable var toma los valores de los subíndices
de array, no sus valores, para leer los valores tenemos que hacer array
[var].

Ejemplo: Un programa que calcula cuantas terminales utiliza cada usuario.

$ who | mawk \'{ contador[]++ }
> END { for(usuario in contador) print usuario, contador[usuario] }\'
ariel 3
root 1

Se pueden eliminar elementos de una array de la siguiente manera:

delete array[indice]


24.7.Variables predefinidas.

Estas variables están definidas y
toman valores automáticamente que resultan útiles.



FILENAME

Nombre del archivo de entrada actual.

FS

Carácter separador de campos (blanco y/o tabulador por defecto)

RS

Separador entre lineas de entrada, por defecto es un carácter
de nueva linea (\"\\n\").

NF

Numero de campos en la linea leída

NR

Numero de linea total.

OFS

Separador entre campos de salida, por defecto es un espacio.

ORS

Separador de linea de salida, por defecto es un carácter de
nueva linea.

24.8. Algunas funciones internas.

gsub(r, s, t) gsub(r, s)

Sustitución global: Cada coincidencia de la expresión
regular r en la cadena t es reemplazada por la cadena
s,
si se omite t, utiliza {sp_content}. Retorna el numero de reemplazos realizados.

index(s, t)

Si la cadena t esta contenida en s, entonces retorna la posición
donde comienza t dentro de s.

length(s)

Retorna la longitud de la cadena s.

sub(r, s, t) sub(r, s)

Igual que gsub, excepto que solo realiza una sola sustitución.

tolower(s)

Convierte mayúsculas a minúsculas.

toupper(s)

Convierte minúsculas a mayúsculas.

sin (x) cos(x)

Retorna seno y coseno de x.

sqrt(x) exp(x)

Retorna raíz cuadrada y exponencial de x.

print var1, var2, var3 ...

Escribe a la salida estándar el valores de las variables separador
por el carácter OFS, que por defecto es un espacio.

printf(cadena-formato, variable1, varible2, ...)

Igual que la función prinft del Lenguaje C, ver la pagina del manual
de printf para mas información.

getline

Lee la próxima linea y la guarda en {sp_content}, actualiza las variables
NF, NR y FNR

getline < file

Lee desde el archivo file y lo almacena en {sp_content}, actualiza las variables.

comando | getline

Ejecuta el comando, lee desde su salida estándar y lo almacena en
{sp_content}, actualiza las variables.

system(\"comando\")

Ejecuta el comando y retorna su estado de finalización, la entrada
y salida estándar del comando serán las mismas asociadas
a mawk.

mawk posee algunas funciones mas, es muy recomendable que lea
la pagina del manual debido a que conocer todas las herramientas disponibles,
nos ahorrara tiempo de desarrollo de un programa.

Existe otro interprete de AWK, llamado Gawk, presenta muy pocas
diferencias con respecto a mawk.

24.9.Opciones.
-F valor

Establece el separador de campos (variable FS) a valor.

-v var=valor

Asigna valor a la variable
var.

-f archivo-programa.

En vez de escribir el programa AWK entre apóstrofos,
como un parámetro del mawk, se puede escribir en un archivo (sin
los apóstrofos) y luego remplazar \'texto-programa\'
por -f archivo-programa en la linea de comando. Esto lo utilizaremos
en el próximo punto, cuando veamos el ejemplo, porque se utilizo
para hacer que el ejemplo sea mas fácil de entender.

-W interactive

No almacena la salida a un buffer, esta opción es útil
cuando nuestro programa AWK, debe ser interactivo con el usuario, ósea
debe leer comandos y dar una respuesta, como es el caso del ejemplo que
trataremos.

25. Ejemplo: Un robot IRC.

Veremos un pequeño ejemplo de un precario bot o robot IRC, construido
mediante bash, mawk y una aplicación para lograr la
conexión de red, que se llama NetPipes.

25.1 NetPipes.
NetPipes son un conjunto de aplicaciones que se pueden utilizar
para construir un modesto cliente o servidor TCP mediante un script bash.

Instalación:

Pueden bajar los fuentes de netpipes desde:

ftp.cis.ufl.edu:/pub/depot/archive-old/netpipes-4.2.tar.gz

La instalación es muy sencilla, primero descomprimimos:

$ tar -zxvf netpipes-4.2.tar.gz


Luego compilamos:

$ cd netpipes-4.2
$ make


Y por ultimo lo instalamos (con el root):

$ su
Password:
# make install


Listo para usarlo !!

25.2.Uso de NetPipes.

NetPipes contiene 6 aplicaciones:

faucet

Se utiliza para crear un pequeño servidor TCP mediante
un script bash, espera intentos de conexiones en un puerto especificado,
cada vez que recibe una conexión, realiza un fork para brindarle
servicio al cliente que se conecto.

hose

Se utiliza para crear un cliente TCP mediante un script en bash,
se conecta a un host y puerto especificado y luego lanza la ejecución
del proceso que le indicamos.

encapsulate:

Multiplexa varios canales virtuales dentro de un único
socket utilizando SCP.

ssl-auth

Nos provee capacidad SSL para scripts bash

sockdown

Ejecuta la llamada al sistema shutdown() sobre un descriptor
especificado y de una manera especificada.

getpeername

Obtiene información del extremo remoto de una conexión
TCP.

De todos estos programas solo utilizaremos hose, para construir
el robot IRC.

Veamos como utilizamos hose:

hose servidor puerto --in --out comando

hose trata de establecer una conexión TCP con el
servidor
y puerto especificado, luego de que se estableció la conexión,
ejecuta comando.

El parámetro --in le dice a hose que todo lo que
llega a nuestra PC por la conexión TCP establecida lo envíe
a la entrada estándar de comando, y el parámetro --out
le dice a hose que todo lo que el programa escriba a su salida estándar,
lo envíe a través de la conexión TCP.

Podemos indicar ambos parámetros simultáneamente,
de esa manera todo lo que recibamos por la conexión TCP podrá
ser leído por el comando comando en su entrada estándar,
y todo lo que el comando escriba a su salida estándar será
enviado por la conexión TCP.

comando será el mawk, ejecutando un programa que
interpretara el protocolo IRC.

Conociendo esto, ya nos alcanza para entender el ejemplo que sigue,
pero si quiere conocer toda la funcionalidad de este comando y los demas,
por favor lea su correspondiente pagina del manual.

25.2 Protocolo IRC.
Veremos solo algunos detalles de este protocolo, como para poder entender
el manejo básico que realiza el bot, para mas detalle recurrir a
la siguiente URL: http://www.faqs.org/rfcs/rfc2812.html

Pasos para conectarse al servidor IRC.
Lo primero que debemos hacer es establecer la conexión TCP,
con el servidor (digamos irc.ciudad.com.ar) y el puerto 6667 por lo general,
para esto se utilizara el programa hose, que es el que entiende
de sockets.

Luego, debemos registrar la conexión, esto lleva dos comandos
IRC, que deberemos enviarlos a través de la conexión TCP
recién establecida, los comandos para registrar la conexión
son:

NICK

USER :

nickname es el nick que utilizaremos, user es el
nombre del usuario y realname es el nombre verdadero, puede contener
espacios.

mode es un valor numérico que se utiliza para
especificar el modo del usuario.


unused es un parámetro aun no utilizado, puede
ser cualquier cosa.

Ejemplo:

NICK ariel
USER ariel 0 * :Ariel Pereira

El servidor puede ser que nos envíe un mensaje PING en cualquier
momento, puede ocurrir durante la registracion del canal o como sucede
normalmente, luego de un intervalo de inactividad. El PING es utilizado
por el servidor IRC para saber si aun estamos ahí, y espera una
respuesta, que es un PONG, sino nos corta la conexión.

El servidor enviara PING :numero

Y debemos contestarle PONG :numero

Esto deberá ser tenido en cuenta cuando escribamos el
programa en AWK que controla la conexión, sino el servidor nos cortara
la conexión si ocurre un intervalo de inactividad.

Una vez que los comandos de registracion se ejecutan exitosamente, el
servidor nos devuelve un mensaje que es algo así:

:servidor 376 nick :End of /MOTD command

Por ejemplo para irc.ciudad.com.ar:

:prima18 376 ariel :End of /MOTD command

Lo mas importante es el numero 376 en el segundo campo ( para
el mawk :-D), una vez que recibimos el 376 ya podemos listar canales, ingresar
a canales, etc.

Ya tenemos la conexión registrada, el próximo paso
será ingresar a los canales, para esto se utiliza el comando:

JOIN #canal

Por ejemplo:

JOIN #linux

Si queremos listar los canales, debemos enviar:

LIST

y el servidor nos contestara con todos los canales disponibles, uno
por linea, el formato de cada linea es:

:servidor 322 nick NroUsuarios Topic

Por ejemplo:

:prima18 322 ariel #Mirrow_y_Dragon_Team 0 :Soporte ciudad
:prima18 322 ariel #Magnasco 0 :para los alumnos del Osvaldo Magnasco
:prima18 322 ariel #ForoDeSalud 0 :
:prima18 322 ariel #PILUSO 3 :Bienvenido/a a #Piluso
:prima18 322 ariel #Armados 0 :
:prima18 323 ariel :End of /LIST

El numero 322 en la segunda columna nos dice que esa linea contiene
el nombre de un canal, al finalizar la lista de canales, el servidor lo
indica enviando el numero 323.

Para enviar un mensaje a un canal (previo ingreso al mismo), lo hacemos
con el comando PRIVMSG, de la siguiente manera:

PRIVMSG #canal :Mensaje

Por ejemplo:

PRIVMSG #linux :Hola linuxeros !!, como andan ??

Para enviar un mensaje a otro usuario (un privado), es similar al
anterior, pero cambiamos el canal por el nick del usuario destino.

PRIVMSG pepe :Como andas Pepe ?

Cuando alguien ingresa al canal donde estamos se vera así:

:nick!user@host JOIN :#canal

Y cuando lo abandona, así:

:nick!user@host PART :#canal

La lista completa de comandos se puede leer en el texto nombrado
anteriormente, le recomiendo su lectura, ya que la explicación de
todos los comandos IRC es extensa.

25.3. El código del ejemplo.
Al fin llegamos al ejemplo !!, aquí intentaremos explicar
el código del ejemplo, no es tan difícil como parece, con
un poco de paciencia se puede entender sin problemas.

El ejemplo se dividió en tres archivos para que sea mas
fácil de entender, pero puede estar todo en un solo archivo, mas
fácil de usar.

A continuación se verán las 3 partes en que se
dividió el programa, pero también se puede bajar desde el
servidor todo el ejemplo en un solo archivo, desde la aquí.

Esta formado por 3 partes, el programa principal es el archivo
starbot, el cual realiza los siguientes pasos:

1.Nos pregunta servidor IRC donde nos conectaremos (irc.ciudad.com.ar).

2.Solicita nick a usar (starbot).

3. Solo queda indicarle los canales donde ingresara (pueden ser varios),
en este paso nos da dos opciones para hacerlo: si elegimos la primera (Listar
canales disponibles), el bot se conecta al servidor, lee la lista de canales
y la muestra en una ventana checklist, donde podremos tildar los canales
que deseamos ingresar con nuestro bot. La segunda opción (Ingresar
canales manualmente) nos permite escribir los nombres de los canales, separador
por espacios, donde el bot ingresara.

4. El bot se conecta, ingresa a los canales y comienza a realizar las
tareas que le indicamos. Nos muestra información como: nick, canales
donde ingresamos, cantidad de reconexiones, etc.

El archivo starbot, que sigue a continuación, es la parte
principal del bot, los otros dos contienen programas en AWK, que son leídos
cuando el archivo starbot llama a mawk.

El archivo listar_canales es un programa en AWK que registra
la conexión, pide la lista de canales, las almacena en un archivo
y corta la conexión.

El archivo robot, es un programa AWK que registra la conexión,
ingresa a los canales y realiza las funciones que le programemos al bot,
es el que modificaremos a gusto para personalizar los comandos del bot.

------------- Comienzo del archivo starbot --------------------

#!/bin/bash
#############################################################
# Variables personalizables, modifíquelas a sus necesidades #
# Algunas son exportadas, para que puedan accederse desde #
# el mawk #
#############################################################
export NICK=starbot
SERVER=irc.ciudad.com.ar
PORT=6667
export TMP=/tmp
####################################################################
# Función checklist_canales #
# Esta subrutina se conecta al servidor, lee la lista de canales, #
# se desconecta del servidor y muestra un check list mediante #
# dialog para permitir la selección de los canales donde el robot #
# ingresara #
####################################################################
checklist_canales ()
{
dialog --infobox \"Leyendo canales...\" 0 0
# Conectamos al servidor y ejecutamos el programa AWK almacenado
# en el archivo listar_canales
hose $SERVER $PORT --in --out mawk -W interactive -f listar_canales
# Armamos un checklist con los canales disponibles, utilizamos sort
# para ordenar los canales por cantidad de usuarios.
dialog --checklist \"Canales disponibles\" 20 60 14 \\
`cat $TMP/canales_disponibles | sort -k2 -nr` 2> $TMP/canales
}
##############################################################
# Función robot #
# Esta función conecta al servidor y ejecuta el programa #
# AWK contenido en el archivo robot, es el encargado de #
# realizar los comandos y funciones del robot, modifíquelo #
# a su gusto :-)) #
##############################################################
robot ()
{
REC=0 # contador de reconexiones.
while true
do
dialog --infobox \\
\"Ejecutando robot - reconexiones: $REC\\n\\
Server: $SERVER\\nCanales: $CANALES\\n\\
Nick: $NICK\" 0 0
# Aquí conecta y ejecuta el mawk con el programa contenido
# en el archivo robot.
hose $SERVER $PORT --in --out mawk -W interactive -f robot
# Si se desconecta, para por aquí, incrementa la variable $REC y vuelve
# a conectar, notar que existe un while infinito, ósea la única manera de
# cerrar el bot es mediante un CTR-C.
REC=`expr $REC + 1`
done
}
#########################################################
# Programa principal #
# Este es el único programa en bash, los otros dos solo #
# contienen programas en AWK. #
#########################################################
# Solicita el servidor donde se conectara.
while dialog --inputbox \"servidor IRC\" 0 0 $SERVER 2> $TMP/server
do
 SERVER=`cat $TMP/server`
 # Solicita el nick.
 while dialog --inputbox \"Ingrese su nick\" 0 0 $NICK 2> $TMP/nick
 do
 NICK=`cat $TMP/nick`
 # Permite la selección de una de las dos opciones.
 while dialog --menú Canales 9 40 2 \\
 L \"Listar canales disponibles\" \\
 I \"Ingresar canales manualmente\" 2> $TMP/op
 do
 OPCION=`cat $TMP/op`
 # Evalúa la selección mediante un case.
 case $OPCION in
 I)
 # Solicita canales.
 while dialog --inputbox \"Ingresar canales\" \\
 0 0 2> $TMP/canales
 do
 export CANALES=`cat $TMP/canales`
 # Ejecuta la subrutina robot
 robot
 done;;
 L)
 # Llama a checklist_canales para leer
 while checklist_canales
 do
 export CANALES=`cat $TMP/canales`\\
 robot
 done;;
 esac
 done
 done
done
exit 0

---------------- Fin del archivo starbot -----------------------

Este archivo contiene el programa AWK que lee los canales disponibles.

-----------Comienzo del archivo listar_canales ------------------

BEGIN {
contador=0
# registra la conexión, enviando:
# NICK nick
# USER nick 0 * :nick
# las variables de shell exportadas, están almacenadas en el array
# asociativo ENVIRON, se puede leer desde el mawk como ENVIRON[\"variable\"]
printf(\"NICK %s\\n\\
USER %s 0 * :%s\\n\", ENVIRON[\"NICK\"], ENVIRON[\"NICK\"], ENVIRON[\"NICK\"])
}
{
if(==\"PING\") print \"PONG\",  # Respuesta a los PINGs
else if(==376) print \"LIST\" # Si lee 376, entonces ya se registro.
 else if(==322)
 {
 # Si lee 322, entonces la linea contiene un nombre de canal
 canal[contador]=
 cantidad[contador]=
 contador++
 }
else if(==323)
 {
# Si lee 323, el servidor finalizo la lista.
# Se guardan los canales a un archivo.
printf(\"%s %d off\\n \", canal[0], cantidad[0]) >ENVIRON[\"TMP\"]\"/canales_disponibles\"
for(x=1; x>ENVIRON[\"TMP\"]\"/canales_disponibles\"
# El exit produce la finalización de mawk y también de la conexión.
exit
 }
}

---------------- Fin del archivo listar_canales ------------------

Este es el programa AWK que controla el bot, lo único que realiza
es enviar un NOTICE a los que ingresan o salen del canal (JOIN o PART),
pero es fácil personalizarlo e implementar lo que necesitamos.

----------------- Comienzo del archivo robot -------------------

BEGIN {
contador=0
# Registra la conexión.
printf(\"NICK %s\\n\\
USER %s 0 * :%s\\n\", ENVIRON[\"NICK\"], ENVIRON[\"NICK\"], ENVIRON[\"NICK\"])
}
{
if(==\"PING\") print \"PONG\",  # Responde los PINGs
else if(==376)
 # Si lee 376, entonces ya se registro con éxito.
 {
 # Elimina las comillas dobles de la lista de canales.
 gsub(\"\\\"\", \"\", ENVIRON[\"CANALES\"])
 # Divide la variable shell CANALES en campos, que son almacenados
 # en el array canal.
 NroCanales=split(ENVIRON[\"CANALES\"], canal)
 for(x=1; x<=NroCanales; x++)
 print(\"JOIN\", canal[x]) # loop que ingresa a los canales.
 }
 else if(==\"JOIN\" || ==\"PART\")
 {
 # Leyo JOIN o PART, entonces tiene que enviar el NOTICE.
 # elimina de  todo lo que sigue despues del \"!\"
 # para leer el nick que ingreso o salió del canal.
 sub(\"!.*\", \"\", )
 # elimina el primer carácter (\":\"), que no forma parte
 # del nick.
 nombre=substr(, 2)
 # nombre contiene el nick, envía el notice.
printf(\"NOTICE %s : *****> StarLinux <***** www.starlinux.com.ar\\n\", nombre)
 }
}

------------------- Fin del archivo robot ------------------------

25.4.Notas sobre el ejemplo:
Este script solo se creo para fines educativos, ósea que
no se continuara desarrollando, ni existirán versiones futuras,
su objetivo es solamente aplicar lo aprendido durante el tutorial.

Es bastante precario porque no realiza control de errores y
tiene bastantes cosas para arreglar, aquel que quiera utilizarlo para hacer
su propio bot, puede hacerlo.

También es posible que falle con algunos servidores,
las correcciones quedan a cargo del interesado por verlo funcionar :-))

Intente reemplazar en el ejemplo el mawk por el
gawk
pero no tuve éxito, el gawk no se llevo bien con el hose.

26. Bibliografía y despedida.

La documentación mas completa, seguramente se encuentra en
la paginas del manual de cada comando, a veces un poco resumida, pero con
la introducción que se le dio a los temas, comenzaran a ser fácilmente
entendibles.

Si quiere leer un libro, aquí le dejo el titulo de un clásico:

El entorno de programación UNIX - Brian W. Kernighan / Rob Pike.
Este texto ha finalizado, ahora queda la depuración que le
realizaran los lectores :-))

Espero que le agrade, al igual como a mi me agrado escribirlo
y ayudar a crear documentación en nuestra lengua.

Mucha suerte con sus scripts y hasta la próxima !!