\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 <> (;) 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 <> 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 <>. 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 <> 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} .