SHELL SCRIPTS

 

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.