Inicio > Administracion > Escritura de scripts de shell

Escritura de scripts de shell


Escritura de scripts de shell

Hemos llegado a un punto donde podemos realizar tareas más complejas a partir de los comandos aprendidos y es aquí donde radica el poder del intérprete de comandos bash. Como veremos a continuación, el intérprete de comandos es un poderoso lenguaje para realizar script que permitan unir varios comandos para realizar una tarea un poco más compleja (y es el este el poder principal de todo Un*x). El único requisito es tener nociones básicas de programación para poder sacar todo el provecho posible de esta característica del intérprete de comandos. En todo caso, con un poco de práctica y un buen sentido de la lógica se podrán hacer también script poderosos para desarrollar las tareas que requerimos.

Deberemos saber también que con la ayuda solamente de la conjunción de comandos no podremos hacer script verdaderamente interesantes. Por esto se incorporan las construcciones de shell. Estas son las construcciones while, for-in, if-then-fi y case-esac. Existen muchas más pero estas serán las más útiles para nosotros en estos momentos. Para mayor información sobre otro tipo de construcciones seria mejor revisar las páginas de manual del intérprete de comandos bash (man bash).

Empezaremos viendo un uso sencillo de la construcción for-in que tiene la siguiente sintaxis

 for var in word1 word2
 do
   commandos
 done

Para poder usar esto, podríamos realizar una lista de directorios que querramos nosotros de una sola vez

 for dir in /bin /etc /lib
 do
   ls -R $dir
 done

Esto hará un listado recursivo 3 veces. La primera vez que pase por el ciclo, la variable $dir tomará el valor /bin, la segunda será /etc y la tercera /lib.

Podríamos prescindir del par do-done con el uso de llaves ({})

 for dir in /bin /etc /lib
 {
   ls -R $dir
 }

Ya hemos visto anteriormente la idea de argumentos en la utilización de comandos y programas; pero deberemos ver como se realiza la codificación de un script para tomar estos argumentos. Como antes dijimos, los argumentos eran pasados a los programas para que estos lo utilizaran. En la construcción de script veremos lo que se llaman variables posicionales cuyo valor corresponde a la posición del argumento luego del nombre del script. Supongamos que tenemos un script que toma 3 argumentos. El primero será el nombre de un directorio, el segundo el nombre de un archivo y el tercero es una palabra a buscar. Este script buscará en todos los archivos del directorio, cuyo nombre incluya el nombre de archivo que le pasamos como argumento, la palabra que también le estamos pasando. El script se llamara miscript y estará compuesto del siguiente código

ls $1 | grep $2 | while read ARCHIVO
 do
   grep $3 ${1}/${ARCHIVO}
 done

La sintaxis será

miscript [directorio] [nombre_archivo] [palabra]

Aquí tenemos varias cosas para ver. Primero que nada, el uso de las variables posicionales. Como se podrá apreciar el número de la variable, que esta precedido por un signo $, indica la posición del argumento cuando el script es llamado. Solamente se podrán usar 9 variables de este tipo sin tener que emplear un pequeño truco de corrimiento que veremos luego, dado que el 0 representa al nombre del script mismo. Es decir que en este caso la variable posicional $0 valdrá “miscript”. Como se puede ver se han utilizado canalizaciones para poner más de un comando junto. Al final de la construcción se esta usando una construcción while. Esta se usa para repetir un ciclo mientras una expresión sea cierta.

 while ($VARIABLE=valor)
 do
   commandos
 done

En este caso esta siendo usada al final de una canalización con la instrucción read ARCHIVO. Es decir, mientras pueda leer el contenido de la variable $ARCHIVO, continuar. Esta variable $ARCHIVO contiene el resultado de lo que arrojo la canalización del listado con la salvedad de que tenia que contener la palabra que le enviamos como argumento, es así que solo se imprimirán las líneas en las que coincida la palabra a buscar de los archivos que cumplan con los requisitos.

Otra cosa a tener en cuenta es una nueva construcción en este script, ${1}/${ARCHIVO}. Al encerrar un nombre de variable dentro de llaves podemos combinarlas. En este caso forman el nombre del directorio (${1}) y añadimos una / como separador del directorio, y seguido e nombre del archivo donde se aplicara el comando grep con la palabra a buscar $3.

Podríamos hacer que este script sea un poco más documentado. Para esto podríamos asignar las variables posicionales a otras variables para que se pueda entender mejor su uso.

 DIRECTORIO=$1
 ARCHIVO_BUS=$2
 PALABRA=$3

 ls $DIRECTORIO | grep $ARCHIVO_BUS | while read ARCHIVO
 do
   grep $PALABRA ${DIRECTRIO}/${ARCHIVO}
 done

El número de las variables posicionales que pueden usarse en un script, como antes dijimos, se encuentra restringido a 10. ¿Qué pasaría si tenemos más de 9 argumentos? Es aquí donde tenemos que usar la instrucción shift. Esta instrucción mueve los argumentos hacia abajo en la lista de parámetros posicionales. De esta manera podríamos tener una construcción con esta distribución de variables

 DIRECTORIO=$1
 shift
 ARCHIVO_BUS=$1

De esta manera podríamos asignar el valor de la primer variable posicional a la variable DIRECTORIO y luego el siguiente argumento que habíamos dado se tomara otra vez con el nombre de $1. Esto solo tiene sentido si asignamos las variables posicionales a otra variable. Si tuviéramos 10 argumentos, el décimo no estaría disponible. Sin embargo, una vez que hacemos el que las variables se corran de posición este se convertirá en el noveno y se accederá por la variable posicional $9. Existe una forma también de pasar como argumento a la instrucción shift el número de posiciones que queremos correr. Por lo cual podemos usar

 shift 9

y así se lograra que el décimo argumento sea el parámetro posicional 1.

Lo que ocurre con los anteriores 9 argumentos es que desaparecen si no se los asigno a una variable anteriormente. Podremos cambiar usar un nuevo parámetro que podrá contener mas de un parámetro pasado al script. Este se denomina $* y contendrá el resto de los argumentos que se pasen al script luego de que se haya realizado un corrimiento determinado. Por ejemplo, si quisiera buscar una frase en lugar de una única palabra el script podría ser

 DIRECTORIO=$1
 ARCHIVO_BUS=$2
 shift 2
 PALABRAS=$*

 ls $DIRECTORIO | grep $ARCHIVO_BUS | while read ARCHIVO
 do
   grep "$PALABRAS" ${DIRECTRIO}/${ARCHIVO}
 done

Lo que aquí cambio es que luego de haber asignado a variables los parámetros posicionales 1 y 2 las variables fueron desplazadas dos veces, eliminando los dos primeros argumentos. Luego asignamos los argumentos restantes a la variable PALABRAS. Para que sea tomado como una cadena, se lo encerró entre comillas para ser pasado al comando grep, si no lo hiciéramos el bash vería nuestra entrada como argumentos individuales para pasar al grep.

Otro parámetro que es de utilidad es el $# que lleva la cuenta de la cantidad de argumentos pasados al script. De esta forma podríamos realizar un script que identificara si se le están pasando la cantidad de parámetros que realmente necesita y anunciar el error si faltaran estos. Para ello utilizaremos la construcción if-then-fi que es muy parecida a la while-do-done, en donde el par if-fi marca el final de un bloque. La diferencia entre estas construcciones es que el if solo evaluara una vez la condición. La sintaxis es la siguiente

 if [ condición ]
 then
   hacer_algo
 fi

Las condiciones que puede usarse se encuentran en las man page test (man test). Nosotros usaremos una simple condición para contar argumentos, pero pueden ser usadas distintas condiciones como nombres de archivos, permisos, si son o no directorios, etc. Para saber si la cantidad de argumentos que se nos a pasado en el script es correcta, utilizaremos una opción aritmética que compare la cantidad de argumentos pasados ($#) con un número que nosotros estipularemos, en este caso 3. Pueden usarse diferentes opciones con el formato arg1 OP arg2, donde OP será alguno de los siguientes

-eq     es igual
-ne     no es igual
-lt     menor que
-le     menor que o igual
-gt     mayor que
-ge     mayor que o igual

Se usará en este caso el -ge (mayor o igual que) dado que si la cantidad de argumentos que siguen al segundo es mayor la tomaremos como una frase a buscar y si es igual como una palabra. Lo único que haremos en caso de que la cantidad de argumentos sea menor, será informar de esto y de la forma de usar el script.

 DIRECTORIO=$1
 ARCHIVO_BUS=$2
 shift 2
 PALABRAS=$*

 if [ $# -ge 3 ]
 then
   ls $DIRECTORIO | grep $ARCHIVO_BUS | while read ARCHIVO
   do
     grep "$PALABRAS" ${DIRECTRIO}/${ARCHIVO}
   done
   else
     echo "Número de argumentos insuficientes"
     echo "Use: $0 <directorio> <archivo_a_buscar> <palabras>"
   fi

Otra utilidad para del if, es la posibilidad de realizar lo que se denomina if anidados. De esta forma podríamos tener varias capas de if-then-else-fi. Como ejemplo podría ser esta una construcción válida

 if [ $condicion1 = "true" ]
 then
   if [ $condicion2 = "true" ]
   then
     if [ $condicion3 = "true" ]
     then
       echo "las condiciones 1, 2 y 3 son ciertas"
     else
       echo "solo son ciertas las condiciones 1 y 2"
     fi
   else
     echo "condición 1 es cierta, pero no la 2"
   fi
 else
   echo "la condición 1 no es cierta"
 fi

Podríamos también hacer que una sola variable tome diferente valores e interactuar con ella para ver si se cumple la condición buscada. De esta forma podríamos por ejemplo hacer un menú de usuario con distintas alternativas. Pero esta forma es útil solo para pocas condiciones. ¿Que pasaría si tuviéramos muchas condiciones mas que agregar? Se nos haría por demás de complicado seguir el esquema armado y sería demasiado código para lo que se trata de realizar. Es aquí es donde se necesita la estructura case-esac. Como se podrá ver, al igual que en el if-fi aquí el inverso de case (esac) cierra la construcción. Veamos un ejemplo de una construcción con case

 read ELECCION
 case $ELECCION in
   a) programa1;;
   b) programa2;;
   c) programa3;;
   *) echo "No eligió ninguna opción valida";;
 esac

Hay que tener en cuenta algunas cosas respecto a este tipo de construcción. Por ejemplo el mandato que le damos al principio read indica al bash que tiene que leer lo que se ingrese a continuación y lo guarde en una variable que se llamara ELECCION. Esto también será útil para el uso de otras construcciones ya que el read no es propiedad exclusiva de la construcción esac, sino que pertenece al mismo bash. Como se ve, se le indica que si el valor que la variable contiene es igual a alguna de las mostradas debajo se ejecute determinado programa. (case $ELECCION in). La elección se debe terminar con un paréntesis “)” para que se cierre las posibilidades. Podríamos poner más posibilidades para cada elección; lo único que hay que recordar es cerrar con un paréntesis. El punto y coma nos marca el final de un bloque, por lo que podríamos agregar otro comando y se cerrara con punto y coma doble al último. El asterisco del final nos indica que se hará en caso de que no coincida lo ingresado con ninguna de las posibilidades. Un ejemplo, sería que nuestra construcción reconozca mayúsculas y minúsculas y además ejecute más de un comando por bloque.

 read ELECCION
 case $ELECCION in
   a|A)
        programa1
        programa2
        programa3;;
   b|B)
        programa4
        programa5;;
   c|C)
        programa3;;
   *)
        echo "No eligió ninguna opción valida";;
 esac

También se podría haber incluído un rango de posibilidades

 echo "Ingrese un caracter: "
 read ELECCION
 case $ELECCION in
   [1-9]) echo "Usted ingreso un número";;
   [a-z]) echo "Usted ingreso una letra minúscula";;
   [A-Z]) echo "Usted ingreso una letra mayúscula";;
 esac

Hay que recordar que todos estos script podrán estar en un archivo, pero para que se ejecuten se le deberá primero dar los permisos pertinentes. Un par de cosas a tener en cuenta para la construcción de script son la forma en que se quiere que ejecute éste y la captura de teclas. Al ejecutarse un script de shell, se estará creando un bash hijo que lo ejecutará. Dado que las variables y funciones pertenecen al intérprete de comandos que las creó, al finalizar el script el proceso hijo del bash morirá y con el todos los seteos de variables y funciones. Por esto, si se quisiera que los cambios de las variables y las funciones que se definieron permanezcan para ser utilizables una vez que el script haya terminado, se deberá comenzar a ejecutar el script con un punto “.” seguido por un espacio antes del nombre de éste. De esta forma el proceso del intérprete de comando actual sera quien ejecute el script con lo que se conservaran todas las variables y funciones.

[shrek@pantano:~]$ . miscript

Un script puede dejar cosas sueltas antes de terminar si éste es finalizado bruscamente enviándole una señal de finalización [1] ya sea con la combinación de teclas CtrlC o con un kill -15. Para esto se deberán capturar estas señales para poder hacer una limpieza, ya se de variables o archivos, antes de finalizar. La forma de hacerlo es con el uso del comando trap; de esta forma se capturará la señal que se le envíe al script y se podrá ya sea ignorar la misma o ejecutar otro comando de limpieza. Para demostrar esto haremos un pequeño script que servirá de menú. La llamada al script del menú podría estar en el archivo .profile del usuario o en el .bash_profile. Si lo que no queremos es que el usuario salga del script con usando la combinación de teclas CtrlC, lo que haremos es capturar la señal y hacer que se ejecute nuevamente el script que se llamará simplemente menu.

 trap './menu' 2
 while :
 do
   echo 'a) Listado de archivos'
   echo 'b) Día y hora actual'
   echo 'c) Mes actual'
   echo 'Seleccione: '
   read ELECCION
   case $ELECCION in
     a|A)      ls;;
     b|B)      date;;
     c|C)      cal;;
     *)      echo "No eligió ninguna opción valida";;
   esac
 done

Como se ve al principio del script se utiliza el comando trap que al captura la señal 2 (SIGINT) que produce el CtrlC relanza el script. Al final del script se ve que se llama nuevamente dado que al ejecutarse el comando de cada elección se quiere que el menú siga funcionando. Practicar con estas construcciones será de gran ayuda para entender el proceso de construcción de script y los preparara para script más complejos usando otros interpretes como el sed, awk y el lenguaje perl. Para mayor información respecto a la construcción de script, remitirse a las páginas de manual del intérprete de comandos, en este caso man bash.

Variables autodefinidas por shell-script

$# —- Esta variable contiene el numero de argumentos que hemos pasado al script.
$0 —- Esta variable contiene el nombre de el shell-script que se ejecuta.
$* —- Esta variable muestra una cadena con los parámetros que hemos pasado al script.
$? —- Esta variable contiene la salida del comando anterior donde se encuentre dentro de el script.

Esto lo veremos mas claro añadiendo estas variables a nuestro script anterior

#!/bin/bash
#Paso de argumentos de forma ‘estática’.
echo “primera opción $1”
echo “segunda opción $2”
echo “tercera opción $3”
#Variables autodefinidas
echo “se ejecuta $0”
echo “tengo $# argumentos”
echo “los cuales han sido $*”
cp #este comando necesita parámetros, por lo tanto lanzara un error que $? capturara
echo “salida de error: $?”

Al ejecutar nuestro script, obtendremos la siguiente salida:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script2a primera segunda tercera
primera opción primera
segunda opción segunda
tercera opción tercera
se ejecuta script2a
tengo 3 argumentos
los cuales han sido primera segunda tercera
cp: falta un fichero como argumento
Pruebe `cp –help’ para más información.
salida de error: 1
rh3nt0n@rh3nt0n-laptop:~/scripts$

Como se puede apreciar las variables autodefinidas del script toman el valor de los parámetros y las circunstancias en las que se ejecuta el script, variando si estas circunstancias varían.

Comando para operaciones aritméticas

comando ‘expr’. Este comando suele usarse con asiduidad dentro de condicionales tanto simples como múltiples que explicare mas adelante, o para simplemente hacer operaciones aritméticas. Necesita como mínimo tres argumentos, dos operandos y un operador.
Los operadores son los típicos de operaciones aritméticas :
suma ———- ‘+’
resta ——— ‘-‘
multiplicación- ‘*’Este símbolo hay que escaparlo ‘\*’ dada su condición de carácter especial.
división——- ‘/’
modulo——— ‘%’ el modulo no es mas que el resto de una división, el cual suele ser el divisor menos uno.
Ejemplo de modulo: 100 % 5 ——> el modulo 5 de 100 sería de 0 a 4.Esto nos puede servir a la hora de generar un numero aleatorio, reducirlo a la cantidad que deseemos, Se entenderá mas adelante con algunos ejemplos.

Sintaxis: expr [operando1] operador [operando2]

Ejemplo de expr:

#!/bin/bash
#Ejemplo de expr (el cuadrado de un numero)
echo “Dame un numero.”
read numero
resultado=`expr $numero \* $numero` # declaramos la variable resultado, como resultado de expr
echo “El resultado de el cuadrado de $numero es $resultado”

La salida del script seria la siguiente:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script5
Dame un numero.
6
El resultado de el cuadrado de 6 es 36
rh3nt0n@rh3nt0n-laptop:~/scripts$

Como se puede observar read pide la variable numero, y expr se encarga de dar como valor a la variable resultado, la operación que realiza con la variable numero, la cual en este caso no es mas que una multiplicación por si misma. Como se puede también observar ‘escapamos’ el carácter asterisco ‘\*’ para que la shell no lo tome como metacarácter.

TEST

Comando ‘test’. Este comando para que no cause confusión desde el principio se suele abreviar usando solo corchetes [cadena valor cadena ] y suprimiendo el propio comando ‘test’, dentro de un ‘if’, ‘case’, ‘until’, ‘for’ etc … (que veremos mas adelante) o simplemente para definir una variable, variable=`[ $1 != $2 ]`
Como veo que me enredo mucho, veamos un ejemplo con test, con y sin comando.
Un sencillo ejemplo de ‘test’para ver su sintaxis:

#!/bin/bash
# ejemplo de test
test $1 = $2 # necesitamos dos argumentos al lanzar el script
echo $? #si ‘echo’ devuelve un 0 es que el comando test es verdadero.

Si lo ejecutamos introduciendo dos valores iguales como argumentos, el resultado de la comparación será verdadero, ya que evaluamos si $1 es igual ‘=’ a $2, por lo tanto ‘echo’ imprimirá un 0 en la pantalla:

rh3nt0n@rh3nt0n-laptop:~$ bash script6 uno uno
0
rh3nt0n@rh3nt0n-laptop:~$

En caso de que introduzcamos dos argumentos distintos, la comparación no será verdadera, y por lo tanto nos devolverá un 1:

rh3nt0n@rh3nt0n-laptop:~$ bash script6 paquito paquita
1
rh3nt0n@rh3nt0n-laptop:~$

Ahora probemos a editar el anterior script de la siguiente manera, y lo ejecutamos del mismo modo que el anterior:

#!/bin/bash
# ejemplo de test con corchetes
[ $1 = $2 ] # necesitamos dos argumentos al lanzar el script
echo $? #si ‘echo’ devuelve un 0 es que el comando test es verdadero.

Una vez ejecutado el script, nos daremos cuenta que es lo mismo poner los corchetes que la palabra test.

Como dijimos anteriormente, el comando test sirve para evaluar ficheros, cadenas de texto, y números, pero estos no se evalúan de la misma forma ya que evidentemente no son argumentos del mismo tipo, y cada uno se trata de forma diferente:

Modos de evaluar con ‘test’.

1º Evaluando ficheros.
Los ficheros se pueden avaluar con estos parámetros:
-f———–indica si el fichero existe.0 si es verdadero y 1 si es falso.
-s———–indica si el fichero existe y no esta vacio.0 si verdad, 1 si falso.
-r———–indica si tiene permiso de lectura.0 verdad, 1 falso.
-w———–indica si tiene permiso de escrituta.0 verdad, 1 falso.
-x———–indica si tiene permiso de ejecución.0 verdad, 1 faslo.
-d———–indica si es directorio. 0 verdad, 1 falso.
Estos parámetros se pueden unir usando los enlazadores :
-a———–and, ‘y’.
-o———–or, ‘o’.
!————not, no.

Veamos un ejemplo de evaluación de ficheros:

#!/bin/bash
# evaluación de ficheros con test.
[ -f $1 ] # al lanzar el script damos ruta relativa o absoluta del fichero.
echo $? #mostrara 0 si exite y 1 si no existe

Probemos con uno de nuestros scripts:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script7 script1
0
rh3nt0n@rh3nt0n-laptop:~/scripts$

Podemos comprobar que existe ya que la evaluación nos retorna un 0.

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script7 script10
1
rh3nt0n@rh3nt0n-laptop:~/scripts$

Podemos comprobar que no existe ya que test nos retorna un uno.
También podemos usar ‘read’ para introducir dinámicamente el fichero a evaluar.

2º Evaluando cadenas de texto.

Para evaluar cadenas de texto tenemos los parámetros:

parámetros:
‘=’ ———- igual
‘!=’———- distinto

Ejemplo:

#!/bin/bash
# evaluación de ficheros con test.
[ $1 != $2 ] # al lanzar el script damos dos cadenas de texto como argumento
echo $? #mostrara 0 si distinto y 1 si si igual

Si ejecutamos el script, nos dará en función de los argumentos una salida u otra.

números aleatorios:

Antes de nada, explicare como se genera un numero aleatorio usando la variable $RANDOM.
La variable $RANDOM genera un numero aleatorio de entre 1 y 32000, podemos necesitar conseguir un numero aleatorio para multitud de funciones, a mi por ejemplo se me ocurre una, los números de la primitiva, pero hay un problema, nos sobran muchos números ya que la primitiva solo tiene 48, para solucionar el problema utilizaremos la expresión ‘%’ modulo, la cual nos limita el numero aleatorio a el modulo que elijamos de este, en este caso será 49.
Lo mejor es un ejemplo:

#!/bin/bash
# aleatorio con random ‘primitiva’
alea=`expr $RANDOM % 49` # generamos un numero
aleatorio1=`expr $alea + 1`# nos aseguramos que no sea 0 sumándole 1
alea=`expr $RANDOM % 49` # el segundo ..
aleatorio2=`expr $alea + 1`
alea=`expr $RANDOM % 49`
aleatorio3=`expr $alea + 1`
alea=`expr $RANDOM % 49`
aleatorio4=`expr $alea + 1`
alea=`expr $RANDOM % 49`
aleatorio5=`expr $alea + 1`
alea=`expr $RANDOM % 49`
aleatorio6=`expr $alea + 1`
echo “Copia estos numeros:”
echo ” $aleatorio1 – $aleatorio2 – $aleatorio3 ” # imprimimos los tres primeros
echo ” $aleatorio4 – $aleatorio5 – $aleatorio6 ” # imprimimos los otros tres

La salida seria algo parecido a esto:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash aleatorio
Copia estos numeros:
24 – 2 – 29
6 – 33 – 11
rh3nt0n@rh3nt0n-laptop:~/scripts$

Por supuesto hay mas métodos para generar números aleatorios, pero me pareció la forma mas rápida y sencilla de que vierais un ejemplo, espero que halláis captado la idea.
Sigamos con text.

3º Evaluando números

Pensareis que con las opciones anteriores de evaluación de cadenas de texto, también se pueden evaluar números, y en cierto modo si se puede, pero hay que tener algo en cuenta :
[ 04 = 4 ]
Si nos damos cuenta, esa evaluación nos devolvería ‘1’, ya que el ‘=’ evalúa si 04 es igual a 4, y como cadena de texto, no lo es.
Por lo tanto, si lo que queremos es evaluar números, tenemos las siguientes opciones o parámetros:
-lt ——— menor que …
-le ——— menor o igual
-gt ——— mayor que …
-ge ——— mayor o igual
-eq ——— igual
-ne ——— no igual

Visto lo cual haremos un script para adivinar un numero aleatorio, en el cual nos basaremos para ir mejorándolo y para así entender lo que viene a continuación: condicional simple.

#!/bin/bash
# adivinador con evaluación numérica
echo “Adivina mi numero”
read adiv
aleatorio=`date +%S` #creamos un numero aleatorio en segundos de 0 a 59
aleatorio=`expr $aleatorio % 5` # lo reducimos a su modulo 5 (de 0 a 4)
# si nos damos cuenta hemos sobrescrito la variable $aleatorio
# no hay problema en esto …
[ $aleatorio -eq $adiv ] # evaluamos si el numero que hemos introducido es igual a el aleatorio
echo $? # nos imprime si lo es o no: 0 si, 1 no .

Ejecutad el script:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash adivinador
Adivina mi numero:
3
0
rh3nt0n@rh3nt0n-laptop:~/scripts$ bash adivinador
Adivina mi numero:
2
1
rh3nt0n@rh3nt0n-laptop:~/scripts$

Como se puede apreciar, en la primera ejecución he tipeado el numero 3, y he acertado, por consiguiente la salida de el comando test es 0.
Por contra, en la segunda ejecución, he tipeado 2, y he fallado, por consiguiente la salida ha sido un 1.

Bueno después de todo esto, espero que halla quedado claro lo que significa un 0 y lo que significa un 1 en la salida de de un test…sigamos

Condicional simple.

Cuando hablamos de condicional simple, básicamente hablamos de el uso de el comando ‘if’, el cual nos permite usar el ‘test’ para que dependiendo de la salida de este, nuestro script haga una cosa u otra.. veamos un ejemplo con el anterior script:

#!/bin/bash
# adivinador mejorado con if
echo “Adivina mi numero:”
read adiv
aleatorio=`date +%S` #creamos un numero aleatorio en segundos de 0 a 59
aleatorio=`expr $aleatorio % 5` # lo reducimos a su modulo 5 (de 0 a 4)
# si nos damos cuenta hemos sobrescrito la variable $aleatorio
# no hay problema en esto …
if [ $aleatorio -eq $adiv ] ; # evaluamos si el numero que hemos introducido es igual a el aleatorio
then # si es igual pasamos a el echo de la siguiente línea
echo “has acertado!!”
else # si no es igual pasamos a el echo de mas abajo
echo “has errado!!”
fi

Si lo ejecutamos:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash adivinador2
Adivina mi numero:
3
has errado!!
rh3nt0n@rh3nt0n-laptop:~/scripts$

Podemos ver que la principal diferencia es que la salida de test nos redirige el camino a tomar por el script hacia un comando u otro, según sea la salida de este falsa o verdadera.

sintaxis de comando ‘if’:

if [ evaluación ] ;

—-then

———paso a tomar si verdadero

—-else

———paso a tomar si falso

fi

Como podréis comprobar, este comando es de los que hay que identar para una mayor comprensión de el a la hora de comprenderlo cuando lo miremos tiempo después de hacerlo.

Respecto a su funcionamiento solo añadir, que en caso de que no se cumpla la condición evaluada, no es estrictamente necesario usar ‘else’, con lo cual la opción ‘else’ es opcional, si lo que nos interesa es una salida verdadera de la evaluación..

Otro ejemplo de ‘if’ pero esta vez evaluando ficheros:

#!/bin/bash
####################################
# Este script evalua dos ficheros dados de forma estatica#
# diciendonos cual es mayor en tamaño .###########
####################################
uno=`ls -l $1 | tr -s ” ” | cut -f5 -d ” “` #filtramos el primer fichero
dos=`ls -l $2 | tr -s ” ” | cut -f5 -d ” “` #filtramos el segundo fichero
if [ $uno -gt $dos ] ;
then
echo ” $1 es mayor ”
else
echo ” $2 es mayor ”
fi

Si ejecutamos nuestro script, evaluando por ejemplo dos de los script que hemos creado hasta ahora:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash ficheromayor /home/rh3nt0n/scripts/script1 /home/rh3nt0n/scripts/script7
/home/rh3nt0n/scripts/script7 es mayor
rh3nt0n@rh3nt0n-laptop:~/scripts$

Condicional múltiple.

Para explicar el uso de esta forma de condicionar, haremos una calculadora simple la cual necesitara de tres argumentos a añadir de forma ‘estática’.
así entenderemos el como y por que de la opción de el comando ‘case’ para poder hacer múltiples condiciones, sin necesidad de alargar el código con ‘if’ recurrentes, como en el siguiente caso:

#!/bin/bash
# calculadora con if.
if [ $3 = “suma” ]
then
expr $1 + $2
else
if [ $3 = “resta” ]
then
expr $1 – $2
else
if [ $3 = “multi” ]
then
expr $1 \* $2
else
if [ $3 = “divi” ]
then
expr $1 / $2
else
echo “opción no valida”
fi
fi
fi
fi

Si ejecutamos la calculadora:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash calculadora 34 56 suma
90
rh3nt0n@rh3nt0n-laptop:~/scripts$ bash calculadora 34 56 multi
1904
rh3nt0n@rh3nt0n-laptop:~/scripts$

Bien, vemos que la calculadora funciona, pero el código es demasiado extenso y además tiene demasiados ‘if’, y no es necesario si usamos ‘case’.

Comando ‘case’.

Sintaxis:(cambiad ‘>’ por espacios)
case variable in
>>>>>>patron1)>>>>ordenes ;;
>>>>>>patron2)>>>>ordenes ;;
>>>>>>patron3)>>>>ordenes ;;
.
.
.
>>>>>>*)>>>>>>>>ordenes ;;
esac

Veamos como ejemplo la calculadora anterior a que se reduce con ‘case’:

#!/bin/bash
# Calculadora con case
case $3 in
“suma”)
expr $1 + $2 ;;
“resta”)
expr $1 – $2 ;;
“multi”)
expr $1 \* $2 ;;
“divi”)
expr $1 / $2 ;;
esac

Al ejecutar nuestra nueva calculadora con la mitad de líneas gracias a case, tenemos que case nos permite dar múltiples valores a una variable y actuar de la forma que sea, según el valor que introduzcamos.

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash calculadora2 50 50 multi
2500
rh3nt0n@rh3nt0n-laptop:~/scripts$ bash calculadora2 50 50 suma
100
rh3nt0n@rh3nt0n-laptop:~/scripts$

Ciclos o loops.

Los ciclos o loops, sirven para hacer que cuando se cumple una determinada circunstancia, una orden se repita, hasta que esa circunstancia deje de cumplirse.
Para esto tenemos varios comandos que comentaremos a continuación:

Comando ‘while’.’mientras’

Este comando ejecuta las ordenes que contenga, si la condición es cierta.(0).

Sintaxis:
while [ condición ]
do
#orden
#orden
.
.
.
done

Veamos un ejemplo:

#!/bin/bash
# El cuadrado de 1 a 100 con while
cont=1
while [ $cont -lt 100 ] #siempre se usa test, se evalua que $cont sea menor que 100
do
cuad=`expr $cont \* $cont` #hacemos que $cuad valga el cuadrado de $cont
echo “el cuadrado de $cont es: $cuad”
cont=`expr $cont + 1` #hacemos que $cont se vaya incrementando en uno.
done

Al ejecutarlo, veremos que while hace que se repitan de la línea 4 a la 8, hasta que la condición $cont -lt 100, se incierta, o sea que $cont valga 100 o mas.

rh3nt0n@rh3nt0n-laptop:~$ bash script8
el cuadrado de 1 es: 1
el cuadrado de 2 es: 4
el cuadrado de 3 es: 9
el cuadrado de 4 es: 16
el cuadrado de 5 es: 25
.
.
.
el cuadrado de 94 es: 8836
el cuadrado de 95 es: 9025
el cuadrado de 96 es: 9216
el cuadrado de 97 es: 9409
el cuadrado de 98 es: 9604
el cuadrado de 99 es: 9801
rh3nt0n@rh3nt0n-laptop:~$

También tenemos la opción de el comando ‘until’ que hace lo mismo que while pero al contrario, o sea su negación.

Comando ‘until’.’hasta’ Negación de while.

Sintaxis:
until [ condición ]
do
ordenes #misma estructura que while
done

Veamos otro ejemplo:

#!/bin/bash
# imprimimos hasta 10 con until
cont=1
until [ $cont -gt 10 ] #hasta que $cont sea mayor que 10
do
echo $cont
cont=`expr $cont + 1`
done

Al ejecutarlo veremos que el bucle funciona hasta que la variable $cont es mayor que 10.

rh3nt0n@rh3nt0n-laptop:~$ bash script9
1
2
3
4
5
6
7
8
9
10
rh3nt0n@rh3nt0n-laptop:~$

Comando de iteración ‘for’.

Este comando es muy útil para evaluar y actuar sobre elementos que estamos pasando por parámetros.

Sintaxis:
for variable in ‘lista de parámetros’
do
ordenes
done

Veamos un ejemplo basico:

#!/bin/bash
# ejemplo basico de for
for numero in $1
do
echo $numero
done

Al ejecutarlo veremos que coge los argumentos y ejecuta uno a uno el comando entre do y done:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script10 “uno dos tres”
uno
dos
tres
rh3nt0n@rh3nt0n-laptop:~/scripts$

Continuemos con for.
Ahora veremos una forma de estructurar for condicionando directamente la variable a introducir por parametros.
Sintaxis:
for (( 1º ; 2º ; 3º ))
do
done
1º Da un valor de inicio a la variable.
2º Condiciona la variable.
3º Incrementa la variable.
Veamos un ejemplo tonto en el que nuestro script nos dirá de un rango entre dos números dados por parámetros la cantidad de números que hay entre ellos y que son menores que el mayor de los introducidos, en caso de que el primer parámetro sea mayor que el segundo se parara el script avisando del error :

#!/bin/bash
# mas opciones de for
if [ $1 -lt $2 ]
then
numero=`expr $2 + 1`
for (( i=$1 ; i<$numero ; i++ ))
do
echo “$i es menor que $numero”
if [ $i = $2 ]
then
exit 0
fi
done
else
echo “opcion incorrecta !! la primera variable a de ser menor”
fi

Si ejecutamos el script veremos que for da el valor a la variable i, la compara con el segundo parámetro dado, si es menor ejecuta el comando echo, y después incrementa el valor de i en uno, haciendo esto hasta que i es igual o mayor que el segundo parámetro:

rh3nt0n@rh3nt0n-laptop:~$ bash prueba 44 45
44 es menor que 46
45 es menor que 46
rh3nt0n@rh3nt0n-laptop:~$

Si la primera variable es mayor que la segunda, nos lanza el error y sale:

rh3nt0n@rh3nt0n-laptop:~$ bash prueba 46 45
opcion incorrecta !! la primera variable a de ser menor
rh3nt0n@rh3nt0n-laptop:~$

Como operar con los ciclos.

Podemos interactuar con los ciclos o bucles según se comporten estos, es decir, si por ejemplo obtenemos una indeterminación dentro de un bucle, podemos hacer que este pare y salte a la siguiente línea del script dejando el bucle, que siga con el bucle, o que el script pare.
Para esto, podemos usar los siguientes comandos:
Comando ‘break’. Si después de una indeterminación en un bucle, la shell encuentra un break, esta lo interpreta saltando el bucle y siguiendo con la siguiente orden después del bucle.
Comando ‘continue’. continue, es un escape, con lo cual, si el bucle termina correcta o incorrectamente, y la shell encuentra un continue, en lugar de proseguir con el siguiente comando después del bucle, vuelve a ejecutar el bucle.
Comando ‘exit’. Aunque exit no es un comando exclusivo de los bucles, también se usa con estos, en el caso de que queramos parar la ejecución del script, si se produce una situación determinada.

Para comprender el uso de estas ordenes, crearemos un script con un bucle que de como resultado una indeterminación. Por ejemplo una indeterminación seria el resultado de dividir un numero entre cero :

#!/bin/bash
# ejemplo de indeterminación
for (( i=-10 ; i<=10 ; i++ ))
do
if [ $i -ne 0 ] ; then
expr 1 \ $i
else
break # continue # exit # aqui probad con cada una de las opciones
fi
done

Ahora probemos estas opciones con un script mas útil, el siguiente ejemplo te dice de un rango de números dados por parámetros, cuales son pares:

#!/bin/bash
# ejemplo con continue break y exit
if [ $# -ne 2 ] ; then
echo “necesito dos argumentos”
exit 1
fi
if [ $1 -gt $2 ] ; then
echo “$1 no puede ser mayor que $2″
exit 2
fi
for (( i=$1 ; i<$2 ; i++ ))
do
mod=`expr $i % 2`
if [ $mod -ne 0 ] ; then
continue # aqui probad primero segun esta y despues con break y exit
fi
echo ” $i es par !!”
done

Al ejecutarlo según esta, la salida será:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script13 4 9
4 es par !!
6 es par !!
8 es par !!
rh3nt0n@rh3nt0n-laptop:~/scripts$

Si cambiamos la línea que contiene el continue, por break o exit, la salida será como esta, ya que el bucle con estas dos opciones quedara inutilizado:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script13 4 9
4 es par !!
rh3nt0n@rh3nt0n-laptop:~/scripts$

Menús selectivos en shell-script.

En shell-script, podemos confeccionar menús con los cuales elegir opciones a ejecutar con solo pulsar una tecla, para esto tenemos el comando ‘select’, el cual siempre debe de ir acompañado de el comando ‘case’.
Sintaxis:
select i in lista de opciones
do
case
esac
done

Veamos un ejemplo:

#!/bin/bash
# ejemplo de select
select i in managed monitor cualquiera_orden salida
do
case $i in
“managed”) echo “aquí podríamos poner la configuración de nuestra interfaz de red en modo managed”;;
“monitor”) echo “aquí podríamos poner la configuración de nuestra interfaz de red en modo monitor”;;
“cualquiera_orden”) echo “aquí podríamos hacer lo que nos diera la gana..”;;
“salida”) break;;
*) exit;; # esta opción hace que con cualquier opción que tecleemos invalida, el script pare.
esac
done

Al ejecutarlo tendremos:

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script11
1) managed 3) cualquiera_orden
2) monitor 4) salida
#? 1
aquí podríamos poner la configuración de nuestra interfaz de red en modo managed
#? 3
aquí podríamos hacer lo que nos diera la gana..
#? 4
rh3nt0n@rh3nt0n-laptop:~/scripts$

Uso de funciones

Una función es un trozo de código que se utilizara con frecuencia en un script, y que solo varia en una variable.
Sintaxis:
nombre_funcion() {
código a ejecutar en la función
}
Veamos un ejemplo:

#!/bin/bash
# determina par e impar de los números que introduzcamos dinámicamente
determinaPar() { # aquí empieza la función
num=`expr $a % 2`
if [ $num -eq 1 ];then
echo “impar ; $a”
else
echo “par: $a”
fi
} # aquí termina la función
echo “Dame tres números”
read a1
read a2
read a3
export a=$a1
determinaPar
export a=$a2
determinaPar
export a=$a3
determinaPar

Si lo ejecutamos veremos que a cada variable introducida se le aplica la función, lo cual ahorra muchas líneas de código ya que sin usar la función, tendríamos que repetir continuamente el código que contiene esta, para cada variable, con lo que esto conllevaría.

rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script12
Dame tres numeros
700
75
12346
par: 700
impar: 75
par: 12346
rh3nt0n@rh3nt0n-laptop:~/scripts$

Captura de señales.

Podemos hacer que nuestro script capture señales a la shell, lanzadas desde el teclado tipo ‘crtl+c’ y que la shell las ignore.
Para esto, se usa el comando ‘trap’.
Comando ‘trap’.Atrapa señales.

Sintaxis:
trap ordenes señal
ó:
trap señal <—- captura señal

Veamos un ejemplo:

#!/bin/bash
# Ejemplo de trap
trap mensaje 2
mensaje() {
echo “no puedes pararme ;)”
}
while [ 1 -eq 1 ] # buble infinito
do
date
sleep 1
done

Al ejecutarlo veremos que con ‘ctrl+c’ no podemos pararlo, podremos ‘pararlo’ usando ‘ctrl+z’, pero no podremos terminarlo hasta que no hagamos un ‘kill %PID’ :

rh3nt0n@rh3nt0n-laptop:~/scripts$ vi script15
rh3nt0n@rh3nt0n-laptop:~/scripts$ bash script15
sáb nov 18 23:46:52 CET 2006
sáb nov 18 23:46:53 CET 2006
no puedes pararme😉
sáb nov 18 23:46:54 CET 2006
sáb nov 18 23:46:55 CET 2006
no puedes pararme😉
sáb nov 18 23:46:56 CET 2006
sáb nov 18 23:46:57 CET 2006
no puedes pararme😉
sáb nov 18 23:46:57 CET 2006
sáb nov 18 23:46:58 CET 2006
sáb nov 18 23:46:59 CET 2006
[2]+ Stopped bash script15
rh3nt0n@rh3nt0n-laptop:~/scripts$ jobs
[1]- Stopped bash script15
[2]+ Stopped bash script15
rh3nt0n@rh3nt0n-laptop:~/scripts$kill %1
rh3nt0n@rh3nt0n-laptop:~/scripts$ jobs
[1]- Terminado bash script15
[2]+ Stopped bash script15
rh3nt0n@rh3nt0n-laptop:~/scripts$ kill %2
rh3nt0n@rh3nt0n-laptop:~/scripts$ jobs
[2]+ Terminado bash script15
rh3nt0n@rh3nt0n-laptop:~/scripts$

Categorías:Administracion
  1. marzo 20, 2013 a las 12:24 pm

    Every weekend i used to go to see this website, for
    the reason that i wish for enjoyment, for the reason that this this site conations truly nice funny stuff
    too.

  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: