next_inactive up previous


Shell Scripting

Enrique Ocaña González
chucky_spain@hotmail.com

Resumen:

Este artículo es una adaptación del libro Bourne Shell Programming, escrito por Robert P. Sayle. En él se explica todos los detalles que es necesario conocer para programar scripts de shell.

1 Entrada/Salida

La entrada y la salida de comandos se puede redirigir y encadenar de las siguientes formas:

TRUCO: Se pueden expandir variables en el texto, de modo que resulta muy fácil hacer plantillas
$ CUENTAS=`ls /home`  
$ for cuenta in ${CUENTAS}; do cat <<FIN  
> He encontrado la cuenta ${cuenta} en el sistema  
> FIN  
> done  
He encontrado la cuenta enrique en el sistema  
He encontrado la cuenta gm en el sistema  
He encontrado la cuenta magic en el sistema

1.1 Algunos ficheros interesantes para Entrada/Salida

2 Agrupación de comandos

Los comandos simples de un shell pueden ser agrupados de las siguientes formas:

3 Expresiones Regulares

Una expresión regular es una secuencia de caracteres que especifica un patrón textual. Se utilizan en muchos comandos de UNIX, como por ejemplo grep, sed, awk y egrep. Mediande ellas se puede indicar a los programas que procesen sólo aquellos elementos que encajen con el patrón. La mayoría de las expresiones regulares utilizan metacaracteres para expresar repetición, existencia o rangos en patrones de caracteres.

A continuación se describen los metacaracteres más comunes:

Este metacarácter sólo se utiliza en los programas sed, grep, egrep y awk:

Estos metacaracteres sólo se utilizan en los programas egrep y awk:

3.1 Compleción de nombres de fichero en el Shell

El shell utiliza algo parecido a las expresiones regulares para emparejar con los nombres de fichero de un directorio (parecido a lo que ocurre en MS-DOS). Este es el juego de metacaracteres propios que utiliza el shell:



Carácter Significado
* (Comodín) Empareja con 0 o más caracteres
? Empareja con 1carácter
[abc...] Empareja con cualquiera de los caracteres listados
[!abc...] Empareja con lo contrario que [abc...]
\ (Escape) Elimina el significado de cualquier carácter especial, incluido fin de línea



4 Entrecomillado (Quoting)

Estos los los tipos de comillas que se utilizan en scripting:

5 Variables

Las variables se declaran con un = sin espacios. A menudo es recomendable utilizar comillas.

Para desreferenciar una variable se coloca un $ delante de su nombre. Para diferenciar el nombre de la variable de lo que viene a continuación es aconsejable colocal la variable entre llaves { }:

#!/bin/sh
NOMBRE='John Smith'
echo $NOMBRE

$ CARA="*Cara*"; CARACOL="*Caracol*"; echo $CARACOL; echo ${CARA}COL  
*Caracol*  
*Cara*COL

5.1 Sustitución de parámetros

La sustitución de parámetros es un método para dotar de un valor por defecto a una variable en el caso de que no esté definida (valga nulo). Existen varios tipos de sustitución de parámetros:

5.2 Propiedades de las variables

Las variables pueden declararse como de sólo-lectura mediante readonly:

$ NOMBRE=Pepe 
$ NOMBRE=Juan  
$ readonly NOMBRE  
$ NOMBRE=Pepe 
bash: NOMBRE: readonly variable
Además, los valores de las variables que se asignan en el shell actual (variables locales) no son heredados por los subshells que lancemos desde el actual. Para que los subshells puedan ver las variables locales es necesario exportarlas:

$ export DISPLAY='localhost:0.0'

5.3 Parámetros especiales

A continuación se muestra una tabla con variables del shell que contienen valores especiales:



Variable Contenido
$0 ... $9 Nombre del ejecutable del shell ($0) y los primeros 9 argumentos
$# Número de argumentos pasados al shell
$* Valor de los argumentos como un único valor
$@ Como $*pero cuando se pone entre comillas dobles, pone cada parám. entre com. dobles.
$$ PID del script o la sesión actual
$! PID del último programa enviado al background
$? Exit status del último programa no ejecutado en background
$- Opciones actuales en efecto



5.4 Lectura de datos para almacenar en variables

Cuando se deseen pedir datos del usuario o de la Entrada Estándar se deberá utilizar el comando read:

$ read PERSONA CIUDAD HOBBY; echo "$PERSONA vive en $CIUDAD y practica $HOBBY"  
Pepe New\ York pesca 
Pepe vive en New York y practica pesca
Los campos en la entrada se separan generalmente por espacios, avances de línea y tabuladores, pero este comportamiento puede ser modificado combiando el valor de la variable de entorno IFS (Internal Field Separator), que contiene los caracteres que actúan de separadores:

$ export IFS=":"  
$ read PERSONA CIUDAD HOBBY; echo "$PERSONA vive en $CIUDAD y practica $HOBBY"  
Pepe:New York:pesca  
Pepe vive en New York y practica pesca

6 Funciones

Las funciones se declaran así:

miFuncion () { 
 echo 'Hola!!' 
 echo 'Soy una función :-)' 
} 
 
otraFuncion () { echo 'Pues yo soy otra función ;-)'; }
El espacio entre el nombre de la función y los paréntesis no es obligatorio, así como tampoco lo es que la apertura de llave aparezca en la misma línea que la declaración de la función. Es importante decir que los argumentos de una función nunca se declaran, de modo que los paréntesis deben aparecer siempre vacíos.

Para llamar a la función, simplemente se le llama, pasándole todos los parámetros que sea necesario. Los parámetros se referencian del mismo modo que en el shell: $1, $2, $3... y el resultado de la función se declara con return y se lee luego con la variable de entorno $?. Si una función no retorna nada, en $? queda almacenado el valor de retorno de la última sentencia que se ejecutó al llamar a la función.

$ felicitacion () { 
> echo "Feliz cumpleaños ${1}!!" 
> return ${2} 
>} 
$ felicitacion Jonh 16 
Feliz cumpleaños John!! 
$ echo $? 
16
Los cambios realizados en las variables dentro de una función no son locales, es decir, permanecen al finalizar la llamada a la función (al contrario de lo que ocurre al abrir un subshell). Del mismo modo, las variables declaradas (mediante una asignación) dentro de una función quedan declaradas para todo el script.

6.1 Inclusión de código

Para incluir (como con el #include de C) el contenido de un fichero (que podría tener muchas funciones útiles definidas) en el script actual se utiliza el comando punto ( . ):

#!/bin/sh 
# FICHERO utilHTML.sh 
cabeceraHTML() { echo '<HTML><BODY>'; } 
cierreHTML() { echo '</BODY></HTML>'; } 
documentoHTML () { 
 cabeceraHTML 
 echo ${1} 
 cierreHTML 
}

#!/bin/sh 
# FICHERO saludo.sh 
. utilHTML.sh 
documentoHTML 'Hola World Wide Web!!'

7 Estructuras condicionales

7.1 Evaluación de condiciones

Existen dos formas de chequear una condición dejando el resultado (0 si es Verdadera o 1 si es Falsa) en la variable $?:

Y estas son todas las posibles comprobaciones que se pueden hacer:



Comprobación Significado
string string no es nulo
-n string string no es nulo
-z string string sí es nulo
string1 = string2 string1 es igual a string2
string1 != string2 string1 no es igual a string2
-eq igual a (eq va en medio de los valores a comparar)
-ne no igual
-gt mayor que
-ge mayor o igual
-lt menor que
-le menor o igual
-b el fichero es un fichero especial de bloque
-c es un fichero especial de carácter
-d es un directorio
-f es un fichero ordinario
-g tiene el Set Group ID Bit activado
-k tiene el Sticky Bit activado
-p es un Named Pipe
-r puede ser leído por el proceso actual
-s tiene longitud distinta de 0
-t el descriptor de fichero está abierto y asociado a un terminal
-u tiene el Set User ID Bit activado
-w puede ser escrito por el proceso actual
-x puede ser ejecutado por el proceso actual
-a hace un AND del argumento anterior y siguiente
-o hace un OR del argumento anterior y siguiente



7.2 Decisión IF

Los IFs se declaran de la siguiente manera:

if [ $n < 5 ]; then 
 echo 'Es...' 
 echo '...menor que 5' 
elif [ $n > 5 ]; then 
 echo 'Es...' 
 echo '...mayor que 5' 
else 
 echo 'Es...' 
 echo '...igual a 5' 
fi

7.3 Decisión CASE

Tambien podemos declarar estructuras CASE, donde se hace pattern-matching con cada caso, que sólo será ejecutado si empareja con la expresión regular:

case ($opcion) in 
 -h) echo 'Este script no tiene ayuda' 
     ;; 
 -n) echo 'El nombre de la máquina es:' 
     hostname 
     ;; 
 *)  echo 'Cualquier opción que no sea -h, -n no es válida' 
     ;; 
esac

8 Bucles

8.1 Bucles FOR

La forma de declarar un bucle FOR es:

for variable in 1 2 3 /tmp/* ` ls -lisa` items_de_una_lista 
do 
 echo 'Un item:' 
 echo $variable 
done
Si se omite la lista de valores del FOR, el shell utiliza la lista de parámetros posicionales $1...$9 condensada en $@:

#!/bin/sh 
# FICHERO imprime_parametros.sh 
echo $@ 
for p 
do 
 echo "Un parámetro: ${p}" 
done

$ imprime_parametros.sh Hola mundo 
Hola mundo 
Un parámetro: Hola 
Un parámetro: mundo

8.2 Bucles WHILE

El bucle se ejecuta mientras se cumpla la condición:

$ i=1  
$ while [ ${i} -le 5 ]  
> do  
> echo ${i}  
> i=`expr ${i} + 1`  
> done  
1  
2  
3  
4  
5
Los bucles WHILE pueden ser utilizados para realizar la técnica conocida como ``desplazamiento de argumentos'':

$ cat printargs  
#!/bin/sh  
while [ $# -gt 0 ]  
do  
 echo "$@"  
 shift  
done  
$ printargs hello world "foo bar" bye  
hello world foo bar bye  
world foo bar bye  
foo bar bye  
bye

8.3 Bucles UNTIL

El bucle se ejecuta hasta que se cumpla la condición:

$ cat printargs2  
#!/bin/sh  
until [ $# -le 0 ]  
do  
 echo "$@"  
 shift  
done  
$ printargs2 hello world "foo bar" bye  
hello world foo bar bye  
world foo bar bye  
foo bar bye  
bye

8.4 Control de las iteraciones

Existen dos comandos para controlar las iteraciones de un bucle:

9 Procesado de argumentos de línea de comando

Tradicionalmente en UNIX existen dos formas de reconocer parámetros:

La interpretación de argumentos se puede hacer cómodamente gracias al desplazamiento de argumentos:

#!/bin/sh  
#  
# setether: set an Ethernet interface's IP configuration  
#  
while [ $# -gt 1 ]  
do  
 case ${1}  
  a) ARP="arp"  
     shift  
     ;;  
  b) BROADCAST=${2}  
     shift 2  
     ;;  
  i) IPADDRESS=${2}  
     shift 2  
     ;;  
  m) NETMASK=${2}  
     shift 2  
     ;;  
  n) NETWORK=${2}  
     shift 2  
     ;;  
  *) echo "setether: illegal option: ${1}"  
     exit 1  
     ;;  
 esac  
done  
INTERFACE=${1}  
ifconfig ${INTERFACE} ${IPADDRESS} netmask ${NETMASK} broadcast ${BROADCAST} ${ARP} 
route add -net ${NETWORK}
Y se puede hacer aún de forma más sencilla utilizando la utilidad getopts:

#!/bin/sh  
#  
# setether: set an Ethernet interface's IP configuration  
#  
while getopts ab:e:i:m:n: option  
do  
 case "${option}" in  
  a) ARP="arp"  
  b) BROADCAST=${OPTARG};;  
  e) INTERFACE=${OPTARG};;  
  i) IPADDRESS=${OPTARG};;  
  m) NETMASK=${OPTARG};;  
  n) NETWORK=${OPTARG};;  
  *) echo "setether: illegal option: ${option}"  
     exit 1  
     ;;  
 esac  
done  
INTERFACE=${1}  
ifconfig ${INTERFACE} ${IPADDRESS} netmask ${NETMASK} broadcast ${BROADCAST} ${ARP} 
route add -net ${NETWORK}
Como el parámetro ``a'' no lleva nada, getopts sabe que no debe buscarle un argumento. Sin embargo, los dos puntos de ``b:'', ``e:'', ``i:``, ``m:`` y ``n:`` indican que esto parámetros sí llevan argumentos, que getopts deberá buscar y colocar en la variable de entorno $OPTARG. Además podemos observar cómo ya no es necesario realizar a mano el desplazamiento (shift) de la lista de argumentos, puesto que de eso ya se encarga getopts.

El uso de getopts agiliza la tarea de interpretar argumentos, aunque tiene desventajas como que sólo puede manejar opciones con un único carácter. Si queremos reconocer, por ejemplo, la opción ``-help'' tendremos que hacerlo a mano.

Sobre este documento...

Shell Scripting

This document was generated using the LaTeX2HTML translator Version 99.2beta6 (1.42)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /home/enrique/Desktop/Notas/bash.tex

The translation was initiated by Debian Linux User on 2000-10-17


next_inactive up previous
Debian Linux User 2000-10-17