Los shell scripts son programas escritos con comandos UNIX y son equivalentes a los batch de DOS aunque mucho más potentes, pues admiten ejecuciones en segundo plano y tienen un conjunto de expresiones mucho más amplio.
Para realizar un shell script en condiciones, al igual que para realizar un programa en cualquier lenguaje de programación, es necesario tener claro lo que se quiere obtener y de que situación se parte. Es mejor perder un poco de tiempo en realizar un desarrollo mental del programa que dedicarse a teclear y probar cosas, pues lo más seguro es que no se consiga el objetivo, y si se consigue la estructura del programa no será la adecuada.
Una de las ventajas que presentan los shell scripts es que pueden ser portadas de una máquina UNIX a otra sin problemas, sin necesidad de retocar nada, salvo que se utilicen llamadas a programas muy concretos específicos de una versión de UNIX, mientras que los programas compilados (desarrollados en C, Pascal, etc.) deben ser recompilados, pues el código se generará en función del microprocesador de cada máquina. Otra ventaja es la facilidad de lectura e interpretación.
El principal inconveniente que presentan respecto a los programas compilados es la lentitud de ejecución, que se puede paliar usando las utilidades built-in incluidas en el propio kernel en lugar de llamar a comandos externos que han de ser leidos de disco. Otro inconveniente es que el código resulta visible a cualquier usuario que lo pueda ejecutar.
Se deben añadir comentarios con el fin de facilitar la lectura del programa. Los comentarios se insertan anteponiendo el carácter "#" al comentario, que se extenderá hasta el final de la línea.
Los scripts suelen encabezarse con comentarios que indican el nombrede archivo y lo que hace el script. Se colocan comentarios de documentación en diferentes partes del script para mejorar la comprensión y facilitar el mantenimiento. Un caso especial es el uso de "#" en la primera línea, seguido del carácter admiración y el path de la subshell, para indicar el intérprete con que se ejecutará el script:
#!/bin/ksh
Los scripts que veremos aquí estan escritos para ksh (korn-shell), si bien, pueden extenderse a otros interpretes con ligeras modificaciones.
Es importante tener en mente que muchos comandos devuelven un valor después de ejecutarse que indicará si la ejecución ha sido correcta o si se ha producido algún error y que tipo de error se ha producido, para en función de ello, poder tomar una decisión u otra. Para conocer si un comando devuelve o no un valor y qué es lo que devuelve en cada caso se deberá consultar la documentación, pero por lo general en caso de una ejecución correcta devolverán el valor 0, y en caso de fallo otro numero, positivo o negativo.
Para poder ejecutar un archivo de comandos es necesario que tenga activados, al menos, los permisos de lectura y ejecución.
A continuación vamos a ver unos ejemplos, comenzando por cosas muy sencillas.
Ejemplo1:
# Programa que cambia de directorio y nos muestra donde estamos en cada # momento. Cambia directorio en un subshell, no altera ambiente original # echo SUBSHELL. DIRACT=`pwd` echo Directorio actual $DIRACT echo Cambiando directorio en el subshell... cd /etc echo Ahora en directorio `pwd` cd echo Ahora estoy en mi directorio, que es `pwd` # fin subsh.cmd
El programa mostraría lo siguiente:
SUBSHELL. Directorio actual /home/jagar Cambiando directorio en el subshell... Ahora en directorio /etc Ahora estoy en mi directorio, que es /home/jagar
Ejemplo 2:
# Programa que muestra la hora del sistema cada segundo durante 1 minuto Cont=0 while [ $Cont -le 60 ] do date ((Cont=$Cont + 1)) sleep 1 done
Ejemplo 3:
# Programa que nos dice el dia de la semana que fue ayer HOY=$(date +%w) #En la variable HOY almacenamos el numero de dia para hoy. ((AYER=$HOY - 1)) # y en ayer, el valor de HOY menos 1 echo "Ayer fue \c" case $AYER in -1) echo "Sabado";; 0) echo "Domingo";; # date +%w devuelve el día de la semana 1) echo "Lunes";; # en formato numerico, con valores 2) echo "Martes";; # comprendidos entre 0 (domingo) y 6 3) echo "Miercoles";; # (sabado). En nuestro caso, ayer tomara 4) echo "Jueves";; # valores entre -1 y 5. 5) echo "Viernes";; *) echo "ERROR: $AYER no existe";;
esac
Ejemplo 4:
#!/bin/ksh # Programa que pide al usuario que introduzca una cadena de caracteres y # la muestra, por pantalla del derecho y del revés. # echo "Introduce una cadena: \c" read NOMBRE LONGITUD=${#NOMBRE} while [ $LONGITUD -gt 0 ] do NOMBREALREVES="$NOMBREALREVES"$(echo $NOMBRE | cut -c$LONGITUD) ((LONGITUD=LONGITUD+1)) done echo "\n$NOMBRE\n$NOMBREALREVES"
El programa mostraría en pantalla:
Introduce una cadena: Hola que tal?
Hola que tal?
¿lat euq aloH
Ejemplo 5:
#!/bin/ksh # Programa que simula una papelera de reciclaje, mediante el cual en lugar de # borrar un archivo, lo que hace es guardarlo en un subdirectorio, con el fin # de evitar borrados accidentales. # if [ $# -gt 0 ] then for i in $* do echo "Moviendo $i al directorio $HOME/borrados" if [ `mv $i $HOME/borrados 2> /dev/null` ¡= 0 ] then echo "Error, no se puede mover $i" fi done else echo "Error: hay que especificar argumentos" echo "$0 archivo1 [archivo2] ..." return 1 fi return 0
Ejemplo 6:
# Programa que ejecuta un proceso largo, y mientras tanto va mostrando un punto en la pantalla, # simulando que el proceso va progresando. function puntos () { if [ $1 ] # Si se ha pasado algun argumento then INTERVALO=$1 # Considerar el intervalo como tal else INTERVALO=5 # Si no se ha pasado, considerar 5 fi while true # Ejecutar siempre, sin fin. do echo ".\c" # Imprimir un punto si salto de linea sleep $INTERVALO # Esperar el intervalo especificado done } # Programa principal # Lo que sea puntos 2 & # Se llama a la funcion puntos, con intervalos de 2 sg [ programa que tarda mucho ] kill -9 $! # Se mata el ultimo proceso lanzado en background # Lo que sea # Fin del programa
Ejemplo 7:
# Programa encargado de comprobar la hora de los diferentes sistemas conectados # a un sistema determinado. En la variable SISTEMAS_TOT se definen los sistemas # de los que se intentara obtener la hora. SISTEMAS_TOT="maquina1 maquina2 maquina3 maquina4 maquina5 maquina6 maquina7" # La funcion hora se encarga de pedir la hora al sistema especificado, # filtrarla y mostrarla por pantalla. hora() { hora=´telnet $1 daytime 2> /dev/null | grep ":" ´ echo "$hora -----> $1" } # Comprobar si el sistema esta accesible, y si lo esta, anadirlo a la variable # SISTEMAS, que sera la que contiene los sistemas accesibles. for SISTEMA in $SISTEMAS_TOT do /usr/sbin/ping $SISTEMA -n 1 | grep " 0% packet loss" > /dev/null if [ $? = 0 ] then SISTEMAS="$SISTEMAS $SISTEMA" else echo "$SISTEMA no esta accesible" fi done #Para los sistemas accesibles comprobar la hora de los mismos en background para minimizar diferencias. for SISTEMA in $SISTEMAS do hora $SISTEMA & done #Esperar a que finalice la ejecucion de todas las tareas en background. wait #Fin de programa
Procesamiento de Opciones (getopts)
Normalmente las opciones son precedidas por los signos - (activa la opción) o + (la desactiva). Las opciones así marcadas pueden ser tratadas mediante getopts.
Sintaxis:getopts [:]opciones [argumentos] variables
Donde:
Ejemplos:
while getopts abc opcion do case $opcion in a) print "Opcion -a";; b) print "Opcion -b";; c) print "Opcion -c";; +a) print "Opcion +a";; +b) print "Opcion +b";; +c) print "Opcion +c";; esac done #funcionara con llamadas como # script -a -b +c # script -ac +b #
USAGE="Sintaxis: $0 -a -b while getopts :ab opcion do case $opcion in a) print "Opcion -a";; b) print "Opcion -b";; +a) print "Opcion +a";; +b) print "Opcion +b";; \?) echo "$OPTARG no es una opcion valida.\n $USAGE";; esac done
Cuando una opción debe ir seguida de un argumento se indica añadiendo : después de la opción, con lo que OPTARG tomará el valor del argumento. También se añade una opción : al case para tratar la falta de argumento.
Ejemplos:
USAGE="Sintaxis: $0 -ac [-b argumento] while getopts :ab:c opcion do case $opcion in a) print "Opcion -a";; b) programa $OPTARG;; # La opcion -b debe llevar un argumento c) print "Opcion -c";; \?) echo "$OPTARG no es una opcion valida.\n $USAGE";; :) echo "La opcion $OPTARG requiere un argumeno.\n $USAGE";; esac done
Se pueden ver más ejemplos y aplicaciones en la sección Ideas y Ejemplos.