Resumen Programacion Shell
Resumen Programacion Shell
1 INTRODUCCIÓN
Una característica añadida del shell es que funciona como un lenguaje de programación:
permite crear ficheros de programas o procesos shell. El contenido de estos ficheros es:
* Comandos UNIX.
* Variables.
* Comandos del lenguaje de programación shell.
Antes de pasar a desarrollar cada parte, vamos a ver las distintas formas de ejecutar un
proceso shell.
Las dos primeras formas son equivalentes: cada vez que se ejecuta el proceso se abre un
nuevo shell que nace y muere con el proceso. Si se ejecuta de la tercera forma esto no ocurre: se
ejecute desde el mismo shell que se abrió con la sesión de trabajo, o sea, desde el que se invocó
al proceso. El resultado más visible de estas dos formas de operar se produce sobre las variables,
ya que estas sólo tienen valor dentro del shell en el que fueron definidas. Por lo tanto, si
ejecutamos el proceso de cualquiera de las dos primeras formas, las variables nacen y mueren
con el proceso. Si se ejecuta de la tercera forma descrita, las variables definidas en el proceso
permanecerán activas hasta que cerremos la sesión de trabajo (mueren con el shell).
Ej
# Guion: practica1
# Autor: Pepe Pérez
# Fecha: 09-03-2005
# Ultima modificacion: 16-03-2005
# Objetivo: Distinguir las tres formas de ejecución de un guión o shell
# Descripcion: asignar valor a variables y mostrarlas
echo dentro
var1=hola
echo $var1
exit
var2=adios
echo $var2
echo fuera
4 VARIABLES
Estas variables pueden crearse y usarse tanto dentro de un proceso shell, como en el
modo interactivo desde el prompt del sistema.
Ej. $ fruta=pera
$ comida=sopa
$ echo $fruta $comida
pera sopa
$
Una variable sólo tendrá valor dentro del proceso en el cual fue creada.
Para observar el valor de todas las variables definidas en un determinado instante, hay
que ejecutar la orden set.
4.1.1 Asignaciones especiales
Programación del shell 3
Ej. $ preposicion=para
$ objeto=${preposicion}caidas
$ echo $objeto
paracaidas
$
* Cuando en el valor asignado existan varias palabras separadas por espacios, hay que
usar comillas para preservar estos espacios en la definición de la variable
* Se puede asignar como valor de una variable, la salida de una orden UNIX. En este
caso, después del igual se encierra la orden UNIX entre caracteres acento grave (`).
Ej. $ hoy=`date`
$ echo $hoy
Mon Jan 16 17:30:19 MET 1995
$
4.1.2 Asignación externa a variables del shell: lectura de valores con READ
Los valores para las variables del shell se pueden capturar a través del teclado o de otros
ficheros mediante la sentencia read, con un funcionamiento similar a la lectura de una variable
en cualquier lenguaje de programación.
También se pueden leer varios datos en varias variables dentro de una misma sentencia
read. Tan sólo hay que poner las variables una tras otra en el mismo mandato read:
1
Será necesario cuando no haya otra forma de diferenciar a la variable del resto de la
cadena de caracteres.
Programación del shell 4
El alcance de una variable es la del guión en la que está definida (o en el shell en el que
esté definida). Si queremos que una variable esté definida para todo el entorno tendremos que
utilizar el mandato export.
El shell tiene predefinidas una serie de variables de entorno. Algunas de ellas pueden
modificarse, asignándolas otro valor distinto al de por defecto. Otras son de sólo lectura, pueden
se usadas, pero no modificadas. Las variables predefinidas modificables más interesantes son:
* HOME. Su valor por defecto es la ruta del directorio del usuario. Es el argumento por
defecto de la orden cd.
* PATH. Es el grupo de directorios donde el sistema busca los ficheros ejecutables.
* PS1. Define la cadena de caracteres que aparecen como prompt del sistema. Por defecto
su valor es $.
• Asignación de valor. Son argumentos que se añaden a la derecha del nombre del
proceso cuando éste es invocado.
Ej. Supongamos que tenemos un proceso que se llama mostrar. Si el proceso se invoca:
Entonces:
* El valor del primer argumento será Pedro y se referencia como $1.
* El valor del segundo argumento será Juan y se referencia como $2.
* El valor del tercer argumento será Pepe y se referencia como $3.
Programación del shell 5
echo argumento1=$1
echo argumento2=$2
echo argumento3=$3
argumento1=Pedro
argumento2=Juan
argumento3=Pepe
El nombre del propio proceso es considerado un argumento y puede ser referenciado con
$0.
Hay un límite de nueve argumentos que pueden ser referenciados: de $1 a $9. Sin
embargo, existe la orden shift que produce un desplazamiento hacia la derecha en la
referenciación de los argumentos:
• $1 para el segundo.
• $2 para el tercero.
• ...
• $1 para el tercero.
• $2 para el cuarto.
• ...
* Y así sucesivamente.
De esta forma es posible escribir un procedimiento que pueda tratar más de 9 argumentos.
Otro modo de acceder a todos los argumentos es con $*. Esta variable se expande a todos los
argumentos especificados cuando se invocó el procedimiento shell.
Los distintos comandos que veremos en este apartado, además de formar parte de un
Programación del shell 6
proceso shell almacenado en un fichero, pueden ser escritos y ejecutados desde el prompt del
sistema. Las peculiaridades de cada uno, al ser usados de esta forma, serán descritas en el
apartado correspondiente.
5.1 echo
En cualquier posición del mensaje puede ir referenciada una cualquiera de las variables
vistas. En su uso normal la variable será sustituida por su valor. Si se requiere que esto no ocurra,
basta con encerrar el mensaje entre comillas simples ( ' ).
* \c. Normalmente echo termina su salida con una nueva línea. Esta secuencia lo evita.
Hay que encerrar mensaje entre comillas.
hola María
* \n. Produce un cambio de línea y un retorno de carro. Hay que encerrar mensaje entre
comillas.
* \t. Produce un salto de tabulación. Hay que encerrar mensaje entre comillas.
Ej. $ echo "mi nombre es:\tCarlos"
mi nombre es: Carlos
$
Programación del shell 7
* \$. Cuando queremos que aparezca el carácter $, hay que usar esta secuencia para evitar
que $ sea interpretado como el símbolo que sirve para referenciar una variable.
Ej. $ precio=10
$ echo el precio del producto es de \$ $precio
el precio del producto es de $ 10
if condición
then
Grupo de órdenes a ejecutar si la condición se cumple
fi
Aunque no tiene mucho sentido, normalmente, su ejecución desde el prompt del sistema,
sirve para ilustrar el proceso, ya que éste es bastante común, como veremos en siguientes
sentencias. Una vez escrito desde el prompt la orden if con la condición, aparece una cadena
secundaria de petición de orden (>). Se sigue escribiendo el resto de la sentencia, y en el
momento que se introduzca la palabra clave de fin de sentencia if, o sea, fi, el shell la ejecutará.
Una vez acabada esta ejecución se vuelve al prompt del sistema.
Para especificar la condición se utiliza la orden test. Básicamente, los argumentos de test
forman una expresión, si la expresión es cierta, test devuelve el valor 0; si la comprobación falla,
la orden devuelve un valor distinto de 0.
En cualquiera de los tres casos, los distintos valores se pueden referenciar mediante
variables. En este caso, si la variable puede no tener ningún valor (un argumento inexistente, por
ejemplo) es aconsejable encerrarla entre comillas, si no, el sistema dará un mensaje de error en el
test, en caso de suceder la situación citada. Aunque no sea necesario en todas las ocasiones, lo
mejor para evitar errores es poner siempre las comillas.
• test sobre valores numéricos. Examina la relación entre dos números. La forma general de la
expresión comprobada es:
test N <primitiva> M
Programación del shell 8
Donde N y M son los valores numéricos a comprobar y las primitivas que pueden usarse son:
* -eq N = M.
* -ne N ≠ M.
* -gt N > M.
* -lt N < M.
* -ge N ≥ M.
* -le N ≤ M.
El shell solo trata números enteros. Los números 1.0 y 1.3, por ejemplo, serán iguales.
En la práctica suele ser más útil analizar condiciones de error. Para negar el sentido dado
a las verificaciones anteriores, se emplea el signo de admiración, que es el operador unario de
negación. Se sitúa antes de la primitiva a negar:
if test ! -d "$1"
then
echo directorio no valido
fi
test S <primitiva> R
Donde S y R son dos cadenas de caracteres. Hay dos primitivas que pueden aplicarse:
2
En este caso, no es necesario encerrar $1 entre comillas, puesto que nunca llegamos a este punto del proceso si la
variable tiene valor nulo, o sea, si no se introduce un parámetro. No pasaría nada si se pusieran.
Programación del shell 10
Ej. Las dos comprobaciones a realizar en los ejemplos anteriores se reduciría a una,
usando esta combinación de comprobaciones.
Añadida a la sentencia if, permite la ejecución condicionada de uno de entre dos grupos
de ordenes. Si una condición se cumple, se ejecutará un grupo de órdenes, si no se cumple el
otro. Sintaxis:
if condición
Programación del shell 11
then
Grupo de órdenes que se ejecutan si la condición se cumple.
else
Grupo de órdenes que se ejecutan si la condición no se cumple.
fi
Ej. Antes de listar un directorio introducido como parámetro comprobamos que este
existe.
Ej. El ejemplo anterior, para diferenciar la situación de error cuando no existe el primer
argumento, de cuando existe pero no es válido, se podría realizar de la siguiente manera:
else
ll $1
fi
Cuando dependiendo del valor de una determinada variable, hay que elegir entre varios
grupos de órdenes a ejecutar, habría que anidar varias sentencias if-else. El uso de la sentencia
case facilita esta tarea. Sintaxis:
case cadena in
cadena_1) si "cadena" es igual a "cadena_1" ejecutar todas estas órdenes
hasta ";;", e ignorar el resto de los casos ;;
cadena_2) si "cadena" es igual a "cadena_2" ejecutar todas estas órdenes
hasta ";;", e ignorar el resto de los casos ;;
...
esac
Se puede hacer uso de las capacidades de correspondencia de patrones del shell aplicando
los metacaracteres ? y *.
Ej. Supongamos que tenemos un proceso shell personalizado, esto es, al ser invocado ha
de incluirse como primer argumento el usuario del proceso. Dependiendo de quien sea éste el
proceso realiza una serie de operaciones diferentes. Si el argumento no es reconocido como
usuario permitido, se indica esta eventualidad y se finaliza la ejecución:
case $1 in
juan)
echo hola juan
(resto de ordenes para ese usuario);;
pedro)
echo hola pedro
(resto de ordenes para ese usuario);;
... (resto de usuarios permitidos)
*)
echo usuario desconocido. Permiso de ejecución denegado;;
esac
Donde:
* variable, es el nombre con se referencia a cada uno de los valores indicados en lista de
valores, dentro de las órdenes especificadas entre do y done. Donde se quiera usar su valor, la
variable se referencia de la forma ya conocida: $variable.
* do, indica el principio del bucle.
* done, indica el final del bucle.
El grupo de órdenes especificadas entre do y done, se ejecuta para cada uno de los
valores indicados en lista de valores. La ejecución de esta sentencia, al igual que el resto de
bucles que veremos, desde el prompt del sistema, puede resultar un tarea de utilidad práctica; no
es infrecuente encontrarnos con la necesidad puntual de ejecutar una serie de operaciones de
forma repetitiva. El proceso que se sigue para su definicióin y ejecución es similar al referido en
el caso de la sentencia if: una vez introducida la primera línea, aparece el prompt secundario (>),
éste se cierra en cuanto al shell se le indique el final del bucle mediante la palabra clave done; en
este momento el bucle es ejecutado, y a su finalización se retorna al prompt del sistema.
Este proceso ordenará el contenido de todos los ficheros con extensiones txt y datos del
directorio actual. El fichero ordenado se almacena con el mismo nombre que el original pero
añadiendo la extensión ord. Cada vez que se ordena un fichero, sale un mensaje indicando tal
eventualidad.
La sentencia continue no suspende el bucle, hace que el resto de las órdenes del bucle
sean ignoradas, y vuelve al comienzo del bucle para efectuar una nueva iteración.
Ej. Supongamos que queremos crear un proceso que nos permite ver el contenido de
unos determinados ficheros introducidos como argumentos al invocar al proceso. Antes de esto
comprobaremos que el fichero existe, de no ser así, iremos al siguiente. Fijamos el número
Programación del shell 14
máximo de argumentos a 3.
for fichero in $1 $2 $3
do
if test ! -s "$fichero"
then
echo el fichero $fichero no existe
continue
fi
cat $fichero
done
Ej. Queremos crear un proceso que ordene el contenido de tres ficheros introducidos
como argumentos. El contenido de los tres ficheros ordenados debe ser almacenado en un único
fichero llamado tres.ord. Si alguno de los tres ficheros no existe debe abandonarse la operación,
indicando esta situación y borrando, si se creo, el fichero tres.ord.
for archivo in $1 $2 $3
do
if test ! -s "$archivo"
then
echo $archivo archivo inexistente
rm tres.ord
break
else
sort $archivo >> tres.ord
fi
done
while condición
do
Grupo de órdenes a ejecutar mientras la condición se cumpla
done
# comienzo del bucle. Se ejecuta hasta que se hayan procesado todos los
# argumentos
until test -z "$1"
do
# antes de operar se comprueba si el fichero pasado como argumento existe
if test ! -s "$1"
then
echo $1 fichero inexistente
shift
else
# si el fichero existe, la operación sobre él del proceso es ver su contenido
cat $1
shift
fi
done
Programación del shell 16
La orden expr evalúa sus argumentos considerándolos como una expresión matemática y
escribe el resultado sobre la salida estándar. Sintaxis:
+ suma
- resta
* producto. Debido a que este símbolo tiene un significado especial para el
shell (metacarácter), cuando se emplee hay que hacerlo de cualquiera de
las siguientes formas: \*, "*" o '*'.
/ cociente
% resto
Cada operador y cada operando forma un argumento distinto para expr, y por ello debe de haber
espacios de separación entre éstos.
Ej. $ expr 13 + 49
62
$ a=3
$ expr $a \* 10 / 2
15
$
Una de las últimas operaciones que realiza el sistema al conectarse un usuario, antes de
que aparezca el prompt de petición de orden, es buscar en el directorio del usuario un fichero
denominado .profile, si lo encuentra lo ejecuta automáticamente. En esta característica radica la
importancia de este fichero: permite ejecutar ficheros y comandos, cada vez que un usuario se
conecta al sistema.
En este fichero se sitúan, entre otras, operaciones tan importantes como:
7 FUNCIONES SHELL
puede estar definida simplemente para el shell, en este caso, morirá su definición con éste (de
forma similar a lo que ocurre con las variables).
nombre de la función ( )
{
ordenes que queremos que se ejecuten al invocar a la función
}
Esta definición se puede realizar dentro de un fichero, o bien desde el prompt del sistema.
En el primero de los casos para que la definición se active para el shell abierto al conectarse al
sistema, el fichero que la contenga tiene que ejecutarse de la forma: . fichero. En el segundo
caso aparecerá, una vez tecleado "nombre de la función ( )", una cadena secundaria de petición
de orden (>). Esta cadena desaparece, y vuelve el prompt del sistema al indicar el fin de la
definición de la función con el símbolo "cerrar llave" (}) (es la secuencia de operaciones típica).
Una vez hecho esto ya se está en disposición de ejecutar la función invocándola por su nombre
La definición hecha de este modo es el caso, ya indicado, en el que la función muere con el shell,
o sea, al cerrar la sesión en la que se definió.
Ej. $ prueba( )
>{
> echo ha ejecutado la función prueba
>}
$ prueba
ha ejecutado la función prueba
$
Para ver las definiciones de funciones activas, se emplea el mismo comando que para las
variables: set.
• Rachel Morgan y Henry McGilton, “Introducción al UNIX Sistema V”, Mc Graw Hill.
• Syed M. Sarwar, Robert Koretsky, Syed A. Sarwar, “El libro de UNIX”, Addison
Wesley, 2001.