Tema 15: Programación en shell (S.O.
UNIX)
16.1.- Mejora de la interfaz de usuario de comandos.
El shell se considera en general un intérprete interactivo de comandos cuando en
realidad se trata de un lenguaje de programación en el cual cada proposición ejecuta un
comando. Aquí se explicarán los principios básicos de la programación shell mediante
ejemplos.
Una aplicación común de un programa en shell consiste en mejorar o modificar
la interfaz del usuario con un programa, lo veremos a través del comando cal().
#cal 10 1983 Imprime el mes de octubre
16.2.- Utilización de la sentencia alternativa.
Resulta incómodo que el mes se tenga que dar numéricamente por eso
trataremos que cal reconozca al mes, para ello utilizamos una proposición case:
case palabra in
patron) comandos;;
patron) comandos;;
....
esac
La proposición case compara la palabra con los patrones de arriba hacia abajo y
ejecuta los comandos asociados con el primer patrón que se reconozca y sólamente ese.
Los patrones se escriben utilizando las reglas para reconocimiento de patrones, cada
acción termina con un doble punto y coma ;;. Esto nos permitirá indicarle el mes Enero
y el asignará el valor de la variable en ese caso 1 y pueda reconocerlo.
1
Resulta útil conocer las variables intrínsecas de shell que se ven en el cuadro a
continuación.
Variables intrínsecas de shell
$# número de argumentos
$* todos los argumentos de shell
$@ igual que la variable anterior
$- opciones suministradas a shell
$? devolver valor del último comando ejecutado
$$ identificación del proceso del shell
$! identificación de proceso del último comando que comenzó con &
$HOME argumento por defecto del comando cd
$IFS lista de caracteres que separa las palabras en los argumentos
$MAIL archivo que cuando cambia transmite el mensaje de que hay correo
$PATH lista de directorios para buscar los comandos
$PS1 cadena de símbolo de espera $ por defecto
$PS2 cadena de símbolo de espera para la línea de continuación de comandos
‘>’ por defecto
Otro problema que encontramos es reconocer el patrón, puede ser que el mes
tenga la primera letra en mayúsculas por lo que para reconocerla tendrán que indicarse
mediante [eE]n*, reconocerá enero, en, Enero...A continuación se dan las reglas para la
identificación de patrones de shell.
Regla de reconocimiento de patrones
* reconoce cualquier cadena, incluso la nula
? reconoce cualquier carácter individual
[ccc] reconoce cualquiera de los caracteres entre paréntesis
“...” reconoce ... exactamente
\c reconoce c literalmente
a|b en expresiones de case reconoce o a o b
Ahora veremos otro comando para que podamos pasar por los directorios
nombrados en PATH que nos permita buscar un fichero ejecutable, mediante for:
for i in (cada componente de PATH)
do
si el nombre está en el directorio i imprimirá el camino
done
La proposición if del shell ejecuta comandos basados en la condición de
terminación de un comando como en:
2
if comando
then comandos si la condición es verdadera
else comandos si la condición es falsa
fi
La parte else es opcional y la localización de nuevas líneas es importante ya que
if, then y else se reconocen sólo después de una nueva línea o un punto y coma.
La diferencia básica con case es que if ejecuta un comando mientras que case
realiza un reconocimiento de patrones directamente en el shell.
Shell proporciona dos operadores para combinar comandos || y && que resultan
más eficaces que if, veamos un ejemplo:
test -f nombrefichero || echo el fichero nombrefichero no existe
Esto equivale a:
if test ! -f nombrefichero
then
echo el fichero nombrefichero no existe
fi
El operador || es un operador condicional que significa OR (disyunción). Con lo
que el comando de la izquierda se ejecuta y si la condición de terminación es cero
(éxito) el comando a la derecha se ignora y si se devuelve de la condición un valor que
no sea cero, el comando de la derecha se ejecuta. El condicional && corresponde a
AND y ejecutará el comando a su derecha sólo si el de la izquierda tiene éxito.
3
16.3.- Procesos repetitivos while y until
En el shell hay tres ciclos: for, while y until. El for es el más usado y ejecuta un
conjunto de comandos (el cuerpo del ciclo) una vez para cada conjunto de palabras, los
ciclos while y until usan la condición de terminación de un comando para controlar la
ejecución de los comandos en el cuerpo del ciclo. Este se ejecuta hasta que el comando
de la condición devuelva una condición de no-cero (para while) e de cero (para until),
while y until son idénticos salvo por la interpretación de la condición de terminación
del comando.
Las formas básicas son:
for i in lista de palabras
do
cuerpo del ciclo $i tiene el valor de elementos sucesivos de la lista
done
for i
por defecto todos los argumentos del archivo del shell, por ejemplo $*
do cuerpo del ciclo $i tiene el valor de argumentos sucesivos
done
while comando
do
cuerpo del ciclo ejecutado a condición de que el comando devuelva verdadero
done
until comando
do
cuerpo del ciclo ejecutado a condición de que el comando devuelva falso
done
El comando condicional que controla un while o un until puede ser cualquier
comando. Podemos ver ahora el uso de variables en un ejemplo:
$ var=hola
$ echo $var
hola
4
Las variables dentro de las llaves especifican un proceso especial de la variable.
Si ésta no se define y si el nombre se acompaña con un signo de interrogación, la
cadena después de ? se despliega y el shell termina
$ echo ${var?}
hola
Para ver la evaluación de las variables del shell
Evaluación de las variables de shell
$var Valor de var, nada si var no está definido
${var} Lo mismo, útil si hay cadenas alfanuméricas que siguen al nombre de
la variable
${var-thing} valor de var si está definido, thing si no lo está
${var=thing} valor de var si está definido de lo contrario thing, si var no está
definido a var se le asigna el valor thing
${var?message} si está definido $var de lo contrario se imprime message
${var+thing} thing si $var está definido, de lo contrario nada
16.4.- Detección de interrupciones
Trap establece una secuencia de comandos que se ejecutan al producirse una
señal:
trap secuencia de comandos númerodelaseñal
Los números de la señal son pequeños enteros: 0 es la salida de shell, 1 corte, 2
interrupción, 3 abandonar, 9 cancelar y 15 terminar (kill() la genera)
La secuencia de comandos de trap deberá llamar explícitamente a exit, ya que de
lo contrario el shell seguirá ejecutándose después de la interrupción. Por lo que la
secuencia de comando será leido dos veces: una cuando el comando trap se activa y una
cuando se invoca. La secuencia de comandos queda protegido mediante apóstrofos, de
manera que las variables se evalúen únicamente cuando se ejecuten las rutinas de trap.
Trap se emplea de forma interactiva para impedir que un programa sea
cancelado por la señal de corte generada por una interrupción telefónica.
5
16.5.- Seguridad en la sustitución de archivos
El comando sort tiene una opción -o para escribir encima de un archivo
$sort fich1 -o fich2 equivale a $sort fich1>fich2
Si los dos ficheros son el mismo, el redireccionamiento con > truncará el
archivo de entrada antes de ordenarlo. La opción -o funciona de forma correcta ya que
la entrada se ordena y guarda en un archivo temporal antes de crear la salida.
Para poder implementar esto en UNIX es preferible usar el operador > y
editaremos un programa ejemplo.
Overwrite deberá pedir confirmación antes de reemplazar el archivo habría que
comprobar que el archivo de destino está vacío.
El ciclo:
Case $# in
0|1) echo “ Sobrescribir fichero cmd’ 1>&2; exit 2
esac
file=$1; shift
new=/tmp/overwr1.$$; old=/tmp/overwr2.$$
trap ‘rm -f $new $old; exit 1’ 1 2 15
if PATH=$opath “$@”>$new
then
cp $file $old # guarda el archivo original
trap ´´ 1 2 15
cp $new $file
else
echo “sobrescribe: $1 falla. $file sin cambios” 1>&2
exit 1
fi
rm -f $new $old
Utilizar sed para remplazar las ocurrencias de una palabra por otra es una
palabra por otra es una maniobra común aunque con el programa overwrite es posible
con unas modificaciones.
For i
do
Overwrite $i sed “S@left@right@g” $i
done
6
16.6.- Detección de interrupciones.
El comando kill se limita a terminar los procesos por la identificación de
proceso. Terminar procesos trae consigo peligros, por lo que una medida de seguridad
consiste en correr siempre zap interactivamente y valerse de pick para escoger a las
víctimas.
Pick despliega argumentos uno a la vez y pide al usuario una respuesta, si la
respuesta es y se despliega el argumento, zap se sirve de pick para verificar que los
procesos escogidos por nombre sean los que el usuario desea terminar.
La diferencia principal entre zap y kill es que zap es interactivo y kill se reserva
para la tarea real. Zap es lento en extremo, el costo de todos los programas adicionales
considerables.
16.7.- Control de modificaciones de archivo
Un programa se desarrolla al írsele añadiendo distintas versiones, por lo que se
recomienda llevar un control de las versiones, en particular si los usuarios usan
versiones en otra máquinas. Por lo demás, conservar copias facilita la tarea de probar
nuevas ideas y si algo sale mal, no cuesta nada volver al programa original.
$diff -e old new
El comando diff genera una lista de comandos de ed que convertirán old en new
por lo que se guardan todas las versiones en el mismo archivo.
El comando get nos servirá para extraer, por ejemplo una versión del archivo
viejo (old) y put servirá para introducir una nueva versión (new)
#get old
#put new