\section{Programación en shell}
\label{seccion:progbash}

Uno de las grandes ventajas que ofrece un intérprete de comandos es la
programación en un lenguaje rústico pero poderoso para automatizar
infinidad de tareas.

\subsection{Introducción}
Como todo lenguaje, posee reglas sintácticas que establecen
la forma de escribir las sentencias a ejecutar.

Para quienes poseen conocimiento de otros lenguajes de programación,
el signo <<punto y coma>> (;) es utilizado frecuentemente como
separador o terminador de sentencias. En \comando{bash} no es
necesario y puede ser reemplazado por \boton{Enter}. Es común encontrar
una línea de este tipo:

\begin{verbatim}
# comando1 ; comando2
(ejecución de comando1 seguido de comando2)
\end{verbatim}

es equivalente a:

\begin{verbatim}
# comando1
(ejecución de comando1)
# comando2
(ejecución de comando2) 
\end{verbatim}

En el primer ejemplo con una sola línea se ejecutan ambos comandos.
Es muy buen ejemplo cuando se quiere encadenar tareas que consumen
mucho tiempo y tienen que ser seguidas.  

Hay que tener presente que no se ejecutan en paralelo. Cuando
termina de ejecutarse \comando{comando1} empieza a ejecutarse
\comando{comando2}.

\subsection{Códigos de salida}

Una vez que termina cada programa, puede brindar al entorno un 
\emph{Código de salida} para que otros programas o el intérprete
sepan como concluyó la aplicación.

Tomemos un ejemplo de la vida de un administrador. Es común que la
administración sea remota, por lo que vamos a considerar que en
nuestra tarea no tenemos conocimiento de lo que está pasando en una
máquina alejada en la que se está ejecutando
\comando{arreglar-base-de-datos}.

El script \comando{arreglar-base-de-datos} es un script que corrige
posibles errores en una hipotética gran base de datos. Y el resultado de
esa corrección interesa, especialmente, si no se pudo arreglar.

Vamos a suponer que hay 2 posibles alternativas:

\begin{description}
  
\item[Salida exitosa] La base de datos no tuvo ningún error. En este
  caso sólo hay que agregar al archivo \archivo{/var/log/BD.registro}
  una línea con la fecha de comprobación y el responsable en ese momento.

%\item[Se efectuaron correcciones exitosamente] La base de datos tenia
%  errores, el script los detecto y los corrigió. Este caso es un poco
%  más grave por lo tanto no sólo hay que agregar al archivo
%  \archivo{/var/log/BD.registro} una línea con la fecha de chequeo y
%  el responsable en ese momento sino también un mail al responsable
%  con los errores encontrados y posteriormente corregidos para que no
%  vuelva a ocurrir
  
\item[Se detectaron errores pero no se repararon] Esta situación es 
  peor. Hay que escribir información detallada
  en \archivo{/var/log/BD.registro} y además enviar correo-e\footnote{por 
ridículo que parezca es la forma correcta de mencionar a los \emph{emails}}
 a una lista  de encargados y directivos de la empresa.
\end{description}

Para diferenciar cada caso se asigna un \emph{código de salida} a cada uno.
Luego de ejecutar \comando{arreglar-base-de-datos} se verifica en base al
código, los comandos a ejecutar.

El algoritmo sería algo similar a:

\begin{verbatim}
if arreglar-base-de-datos
then
  date >> /var/log/BD.registro
  echo $RESPONSABLE_BD >>  /var/log/BD.registro 
else
  informar-errores.sh >> /var/log/BD.registro
  enviar-mail "Errores en BD" lista-encargados lista-directivos
fi
\end{verbatim}
%$

?`Y dónde están los códigos de salida? Bueno, el \emph{comando
  interno}\footnote{\emph{built-in command} en inglés.}  \comando{if}
analiza el código de salida, y si es {\tt 0} (cero) ejecuta la lista
de comandos después del \comando{then}, en caso contrario (y si
existe) la lista de comandos después del \comando{else} hasta
encontrar un \comando{fi}.

Por lo tanto el script \comando{arreglar-base-de-datos} tiene que devolver
{\tt 0} en caso de éxito. \'{E}ste es  el comportamiento normal de la mayoría de los 
comandos en Linux y otros Unix, y un valor para varios errores.

Las páginas \comando{man} suelen tener una sección llamada \textbf{Exit Status}
que contiene los códigos que devuelve el programa.

\subsection{El comando \comando{if}}

Ya vimos un ejemplo sencillo utilizando \comando{if}, que a su vez 
puede ser de gran utilidad. Ya hablamos de la equivalencia 
entre el <<;>> y el <<Enter>> pero hay veces que pasa desapercibido
el detalle de que \comando{if} y \comando{then} deben estar en diferentes
 líneas por lo que: 

\begin{verbatim}
# if COMANDO then COMANDO fi
\end{verbatim}

Este último ejemplo va a dar error de sintaxis. La forma
correcta de expresar es:

\begin{verbatim}
# if COMANDO; then COMANDO ; fi
\end{verbatim}

o bien:

\begin{verbatim}
# if COMANDO
> then COMANDO
> fi
\end{verbatim}

No hace falta crear una archivo que contenga las instrucciones, en cambio,
esta programación se puede ir realizando <<en línea>>. Es decir, introducirla
por línea de comandos en una terminal.

Muchas veces es necesario hacer comparaciones o comprobaciones para
tomar decisiones.  Por ejemplo ``Si el usuario no posee el archivo
\archivo{~/.configuracion} 
%$
con la configuración por defecto'' o bien ``Si el número de archivos
es mayor a 20 escribir {\tt no se puede transferir}''

Existe el comando \comando{test} para hacer estas evaluaciones y en
base al resultado, código de error de \comando{test} será {\tt 0} u
otro número. Por ejemplo, para saber si un archivo
\archivo{.configuracion} existe en el \emph{home} del usuario el comando
puede ser:

\begin{verbatim}
# test -e $HOME/.configuracion
\end{verbatim}
%$

para facilitar la notación dentro del comando \comando{if} se
hace un \emph{enlace simbólico}\footnote{\emph{symbolic link} en inglés, utilizando
el comando \comando{ln -s}}
a un comando llamado \comando{[}. Parece extraño llamar a un comando con un corchete
abierto pero veamos un ejemplo:

\begin{verbatim}
if test -e $HOME/.configuracion
\end{verbatim}
%$

Puede traducirse a:

\begin{verbatim}
if [ -e $HOME/.configuracion ]
\end{verbatim}
%$

donde el \comando{]} \emph{(corchete cerrado)} final no tiene importancia 
y la programación queda menos engorrosa.

Podríamos utilizar lo aprendido para crear un script que
 ``Si el usuario no posee el archivo
\archivo{~/HOME/.configuracion} 
%$
con la configuracion por defecto entonces crearlo.'' en unas pocas líneas:

\begin{verbatim}
if [ -e $HOME/.configuracion ]
then
  crear-configuracion >> $HOME/.configuracion
fi 
\end{verbatim}

El comando test permite la composición de condiciones con
AND y OR lógicos con los modificadores {\tt -a} y {\tt -o}
respectivamente y el modificador NOT con {\tt !}. Se podría agregar a la linea
del \comando{if} anterior la condición ``y además no posee el archivo 
\archivo{SinConfiguracion}'' de la siguiente forma

\begin{verbatim}
if [ -e $HOME/.configuracion -a ! -e SinConfiguracion]
\end{verbatim}
%$

Ejemplos mucho más interesantes de analizar se pueden encontrar en el
directorio \archivo{/etc/rc.d/init.d}\footnote{Este directorio puede
  variar según las distribuciones, también puede ser
  \archivo{/etc/init.d}}.

\subsection{El comando \comando{while}}

El comando \comando{test} se utiliza cuando se itera con
el comando \comando{while}. En este comando es muy útil la
comparación de valores. 

\comando{test} puede comparar números al igual que cadenas de caracteres.

\begin{verbatim}
while [ ${CANT_USUARIOS} -le 1 ] 
do
  echo Todavia no hay suficientes jugadores
  sleep 1
done
echo Ahora hay más de 1 usuario
\end{verbatim}
%$

Este ejemplo comprueba si la variable CANT\_USUARIOS es menor o 
igual ({\tt -le} significa  \emph{less or equal} en inglés) a uno; de ser así,
repite cada 1 segundo, <<{\tt Todavía no hay suficientes jugadores}>>.
En cuanto la cantidad de usuarios sea mayor a 1 sale del ciclo.

También es posible hacer un ciclo infinito utilizando \comando{test}
(o bien llamado \comando{[}) para que devuelva siempre verdadero (con
\comando{[ 1 ]}). Se recomienda usar el comando \comando{true} que 
devuelve un código de salida exitoso (cero) y el \comando{while} no termina 
a menos que se le envíe una señal con \boton{Ctrl-C}.

\begin{verbatim}
while true
do
  clear
  mailq
  sleep 2
done
\end{verbatim}

Este simple algoritmo muestra el contenido de la <<bandeja de salida>> de
\comando{sendmail} cada 2 segundos. Vemos que con pocos conocimientos en
\comando{bash} se pueden lograr infinidad de cosas.

\subsection{El comando \comando{for}}

Para quienes programan en otros lenguajes el comando \comando{for} se
comporta distinto a la clásica sentencia \emph{for}. Este comando
asigna \emph{de} una lista de elementos, el valor \emph{a} una
variable y repite una lista de comandos con esa variable.

Si bien la explicación puede ser un  poco confusa, el concepto es 
bastante fácil de entender al ver un ejemplo.

\begin{verbatim}
for cantidad in dos tres cuatro cinco seis siete
do
  echo ${cantidad} elefantes se balancaban sobre la tela de una araña
  echo como veian que resistía fueron a llamar a otro elefante...
done
\end{verbatim}
%$

\begin{description}
\item[dos (...) siete] son los elementos.
\item[cantidad] es la variable que iteración a iteración va tomando los valores
de la lista de elementos
\item[do; echo (...);done] es el bloque de comandos a iterar.
\end{description}

Esta es la forma más simple de utilizar el comando  \comando{for},
pero con pocas variaciones se puede realizar cosas muy útiles, por ejemplo:

\begin{verbatim}
for archivo in `ls`
do
  touch ${archivo}
done
\end{verbatim}
%$

La lista de elementos se obtiene de el resultado del comando
\comando{ls}.  Es decir, primero se ejecuta \comando{ls}, el cual dará
el listado de todos los archivos de un directorio, y a todos esos
archivos se les aplica un \comando{touch}\footnote{El comando
  \comando{touch} cambia la fecha de modificación de un archivo
a la fecha actual}.

%%%%%%%%%%%%
% Práctica %
%%%%%%%%%%%%
\input{Shell/ProgramacionEnShell-practica}
