Curso de Introducción a Unix - por F.C.

SHELL SCRIPTS

Como se vió al principio del curso, la shell nos permite acceder al kernel del sistema operativo. La shell en reaildad lo que hace es interpretar la línea de comandos y ejecutar los programas que ésta indica. Sin embargo, además de leer la línea de comandos, también puede ejecutar los comandos que se encuentren en un fichero. Cualquier comando que pueda ser tecleado en la línea de comandos puede ser incluido en un fichero al que se le denomina como shell script o shell program.

Un shell script es un fichero donde hemos introducido cualquier comando que se puede ejecutar en la shell. Se pueden incluir comandos UNIX, utilidades, emplear pipes, redireccionar entradas y salidas, definir y utilizar variables, además del uso de una serie de comandos UNIX creados para ser usados fundamentalmente en los shell scripts, como son condicionales y comandos para controlar el flujo del programa.

Estos comandos UNIX especificos de los shell scripts, suelen tener una sintaxis distinta dependiendo de la shell empleada. Hay dos opciones básicas que tienen a su vez algunas diferencias. Por un lado están la Korn shell y la Bourne Again shell, que tienen un lenguaje similar al de la shell original de UNIX (Bourne shell). Por otro lado están la C shell y la TC shell que son muy similares entre si (en el curso utilizaré esta opción aunque cada vez es más frecuente utilizar la Korn shell).

Vamos a empezar con un ejemplo muy sencillo. Creamos un fichero que se llame INFO y escribimos en el los siguientes comandos:

# Script que nos muestra por pantalla la fecha, la hora, la maquina en que
# estamos, nuestro username, nuestro numero identificador de usuario y de
# grupo, quien esta conectado a la maquina y el directorio en que estamos
echo "la fecha de hoy es `date`"
echo "La maquina en que estas trabajando es: $HOST"
echo ""
echo -n "Tu user name es: "
whoami
echo "Los numeros identificadores de usuario y de grupo son:"
id
echo ""
echo "En la maquina estan conectadas las siguientes personas "
who
echo "Estas en el directorio `pwd`"

Luego ponemos el fichero en un directorio al que haga referencia la variable PATH. Normalmente los usuarios tienen definido el directorio bin, donde ponen sus ejecutables y al que hace referncia la variable PATH. Finalmente le damos permiso de ejecuión al fichero.
chmod a+x INFO

Ahora podemos teclear en la línea de comandos desde cualquier directorio
INFO
y se ejecutarán los comandos incluidos en el fichero.

Este es un sencillo ejemplo de shell script en que simplemente se ejecutan una serie de comandos secuencialmente. Sin embargo las posibilidades que permite la programación en shell son mucho más amplias.

El script INFO sólo usa comandos estandard de UNIX que funcionan en cualquier shell. Si vamos a crear un shell script que usa características especificas de una shell en concreto, tenemos que indicar al principio del script en que shell queremos que se ejecute. Para ello se pone en la primera línea del script los caracteres #! seguido de la ruta de acceso al fichero ejecutable que corresponde a la shell. Por ejemplo para especificar que el script se ejecute en una C shell pondremos en la primera línea:
#!/bin/csh
donde /bin/csh es la ruta de acceso al programa que ejecuta una nueva C shell. De esta forma el fichero ejecuta una C shell y los comandos que van acontinuación se ejecutan como subprocesos de esta C shell.

Argumentos, Variables y condicionales

Argumentos de los shell scripts

Los scripts pueden ejecutarse con argumentos introducidos desde la línea de comandos. Para utilizar un determinado argumento en el shell script nos referimos a el con $ seguido de un número que indica la posición en la línea de comandos en que fué introducido. Veamos el siguiente ejemplo:

#!/bin/csh
#Ejemplo para ver como se pasan argumentos a un shell script
#Imprime los argumentos en orden inverso (hasta un maximo de 9) 
echo "los argumentos introducidos son : $9 $8 $7 $6 $5 $4 $3 $2 $1"

Variables en los shell scripts

Dentro de los shell scripts podemos usar el comando set para definir las variables de la misma forma que se hace en la línea de comandos. Veamoslo ahora con más detalle.
set varname=value
set varname[n]=word
value puede ser una palabra (o una cadena cualquiera de caracteres entre comillas), o puede ser una serie de palabras separadas por espacios, en cuyo caso hay que ponerlas entre parentesis). De esta forma se definen variables con indice. Para asignar un valor a un sólo indice se utiliza el segundo formato.

Las variables de la shell utilizada en el script también están definidas y podemos utilizarlas. Además hay una serie de variables con significados especiales que son muy útiles para crear shell scripts, por ejemplo:

Para aclarar todo esto veamoslo con el siguiente ejemplo en el que creo el script ejemp2 que simplemente muestra una serie de variables.

#!/bin/csh
#Ejemplo para ver como se definen las variables y 
#mostrar algunas variables especiales 
set text1="Esta variable tiene solo un indice"
echo "El numero de indices de  la variable text1 es $#text1 y su valor es:"
echo $text1
echo ""
set text2=(Esta variable tiene varios indices)
echo "El numero de indices de la variable text2 es $#text2 y sus valores son:"
echo $text2[*]
echo ""
echo "Has tecleado $#argv argumentos que son: $argv"
echo ""
echo "El valor de la variable path es:"
echo $path
echo ""
echo "La shell en que se ejecutan los comandos del script es $shell"
echo "El PID de este script es $$ y el nombre del comando $0"
echo ""
echo "Teclea una linea en la terminal"
echo "Has tecleado: $<"
El resultado de ejecutar este ejemplo introduciendo como argumentos:
a b c d
y tecleando al final del programa:
Esto es solo un ejemplo
es el siguiente:
El numero de indices de  la variable text1 es 1 y su valor es:
Esta variable tiene solo un indice
 
El numero de indices de la variable text2 es 5 y sus valores son:
Esta variable tiene varios indices
 
Has tecleado 4 argumentos que son: a b c d
 
El valor de la variable path es:
/home/fcabrera/bin /usr/bin /usr/sbin /usr/ccs/bin /usr/dt/bin /usr/openwin/bin 
/opt/local/bin /opt/SUNWspro/bin /usr/ucb /usr/local/bin /usr/X11/bin /opt/hpnp/bin . 
/usr/SUNWwabi/bin /opt/gnu/bin /usr/gipsy/exe/sol4 /net/lisa/lisa3/fcabrera/l3/c84/ /net/lisa/lisa3/fcabrera/c90
 
La shell en que se ejecutan los comandos del script es /bin/csh
El PID de este script es 29897 y el nombre del comando ejemp2
 
Teclea una linea en la terminal
Esto es solo un ejemplo
Has tecleado: Esto es solo un ejemplo

Expresiones aritméticas

La shell nos permite definir variables con valores númericos y realizar determinadas operaciones aritméticas que puden ser muy útiles en los shell scripts. La sintaxis para definirlas es:
@ variable = valor
Los operadores que podemos usar son:

+       Suma
-       Resta
*       Multiplica
/       Divide (parte entera)
%       Resto de una división
Por ejemplo el comando:
@ res = $var1 + 3
Sumará 3 a la variable var1 y se lo asigna a la variable res. También tienen validez para la shell los operadores ++ y -- que suman y restan 1 respectivamente a una variable. Por ejemplo:
@ res ++
Sumará uno a la variable res.

Condicionales

Puede utilizarse el comando if para ejecutar comandos según se cumpla o no una condición. La estructura de un comando if es:

if (expr) then
...
else if (expr2) then
...
else
...
endif

Veamos un ejemplo sencillo:

#Ejemplo para mostrar el funcionamiento de if
#Comprueba si existe un fichero en el directorio, si esta nos ejecuta ls -l
#Si no esta nos dice "no existe un fichero con el nomgre ..." 
ls $1>/dev/null
if ($status != 0) then
        echo "No existe el fichero $1"
else
        ls -l $1
endif

Primero se hace un ls del fichero buscado, enviando la salida al dispositivo /dev/null porque no nos interesa. Luego comprueba si el valor de la variable status es distinto de 0. Esta variable se define automáticamente y su valor depende de si el comando anterior (ls) tiene exito, en cuyo caso tendrá un valor de 0, o da algún error, en cuyo caso su valor será distinto de 0. Así pues, si el fichero existe status valdra 0 y se ejecuta ls -l y si el fichero no existe, status tendrá otro valor y se ejecuta echo "No existe el fichero $1".

Los operadores lógicos y de comparación que se pueden usar en las expresiones son:

OPERADORES LOGICOS
!       NOT
&&      AND
||      OR

OPERADORES DE COMPARACION

==      Igual (enteros o cadenas de caracteres)
!=      Distinto (enteros o cadenas)
=~      La cadena de la izquierda se ajusta al patrón de la derecha
!~      La cadena de la izquierda no se ajusta al patrón de la derecha 
>    Mayor que (enteros)
<    Menor que (enteros)
>=   Mayor o igual (enteros)
<=   Menor o igual (enteros)
Además se pueden usar los paréntesis.

Comandos mas importantes

El comando foreach

Su estructura es la siguiente:

foreach var (wordlist)
        ...
end

La variable va tomando cada uno de los valores de la lista y para cada valor se ejecutan los comandos comprendidos entre foreach y end. Veamos un ejemplo:

#!/bin/csh
#Ejemplo para mostrar el funcionamiento de foreach
#Cambia de minuscula a mayuscula el contenido de los ficheros
#que se introducen como argumentos en la linea de comandos escribiendolos
# en un fichero con el mismo nombre y extension.M
foreach f ($argv[*])
        set name=$f.M
        cat $f | tr 'a-z' 'A-Z' > $name
end

La variable f va tomando cada vez el nombre de uno de los argumentos. Se define luego la variable name que será igual al nombre de entrada con la extensión .M. El comando cat envía el contenido de cada uno de los ficheros de entrada al comando tr (que es el que transforma las minúsculas en mayúsculas) y este envía la salida a el fichero con el mismo nombre y extensión .M.

Vamos a ver un ejemplo de un shell script algo más elaborado que combina foreach e if. El script se ejecuta como:
mata host
Donde host es el nombre de una máquina. El script buscará los PID de todos los procesos que tengamos en esa máquina y los matará con kill -9. El contenido del script mata será:
#!/bin/csh
#Script que mata todos los procesos que tengamos corriendo en una maquina
#La sintaxis es mata seguido del nombre de la maquina
if ($#argv != "1") then
        echo "Introducir como argumento el nombre de la maquina"
        exit
endif
set pid=`rsh $1 ps -feao ruser,pid | grep $user`
foreach pro ($pid)
        if ($pro != $user) then 
                rsh $1 kill -9 $pro
        endif
end

El comando switch

La sintaxis del comando es:

switch (string)
case label1:
...
case label2:
.
.
...
breaksw
...
default:
...
breaksw
endsw

Se va comparando si las etiquetas label1, label2 ... coinciden con la cadena string, en cuyo caso se ejecutan los comandos que van a continuación. en el momento que se encuentre un breaksw continuará con los comandos que siguen después de endsw. Si no se encuentra ningún breaksw se llega a default: y se ejecutan los comandos que siguen. Si tampoco hay default: el programa continua su ejecución después de endsw.

El comando while

La sintaxis es:

while (expr)
...
end

Su funcionamiento es muy simple. Mientras la evaluación de la expresión entre parentesis sea verdadera (distinto de 0) se ejecutarán los comandos comprendidos entre while y end.

Otros comandos

Existen otros comandos que son muy útiles en la programación en shell, pero que su explicación queda fuera de los objetivos de este curso. Para los que quieran profundizar en el tema se muestran a continuación los más importantes con un ejemplo sencillo:

awk (pattern scanning and proccesing language)
Permite buscar patrones de caracteres en ficheros y realizar una serie de acciones cuando encuentra una coincidencia entre el patrón y una línea. Ejemplos:
awk '$4 ~/agn/ {print $1,$9}' log
Busca el patrón agn en la columna 4 de cualquier línea del fichero log, y para las líneas en que encuentre el patrón realiza la acción indicada entre {}, que en este caso es mostrar la columna 1 y la 9.

sed
Permite modificar el contenido de un fichero (sustituir palabras, añadir, borrar, etc) según indiquen una serie de comandos. Un ejemplo sencillo es:
sed -e "s/[Gg]alaxia/NGC4151/g" file
Remplazará en cada línea del fichero file la palabra galaxia o Galaxia por NGC4151.

cut and paste
Cortan y pegan campos seleccionados de cada línea de un ficheros. Ejemplos:
cut -c 1-5 file1
paste l1 l2
El primero extrae por la terminal los cinco primeros caracteres de cada línea del fichero file1. El segundo une la línea los dos ficheros l1 y l2 mostrandolo por la terminal.