Esta página puede ser redistribuida libremente bajo los términos de la licencia GPL. Vease ( GPL texto original ) o si lo prefiere (Traducción española no oficial de la GPL) Al margen de las obligaciones legales que se derivan del uso de esta licencia rogamos sea respetada la referencia a su lugar de publicación original www.ciberdroide.com. y a su autor original Antonio Castro Snurmacher (Madrid 01/01/2000). |
![]() |
Esta ausencia de garantía se hace extensa a cualquier tipo de uso de este material y muy especialmente a las prácticas, ejercicios, y de ejemplos que encuentre en estas páginas. Deberá trabajar siempre salvo indicación contraria con un SO Linux y con un usario distinto de 'root' sin privilegios especiales. Como directorio de trabajo se procurará usar el directorio '/tmp' o algún otro que no contenga información valiosa. Tampoco se considera buena idea practicar en una máquina que contenga información valiosa. Todo esto son recomendaciones de prudencia. En cualquier caso si algo sale mal toda la responsabilidad será únicamente suya. En ningún caso podrá reclamar a nadie por daños y perjuicios derivados del uso de este material. Para más información lea el contenido de la licencia GPL y abstengase de hacer prácticas si no está dispuesto a asumir toda la responsabilidad.
|
Sentencia condicional 'if'
En lugar de _condición_ podríamos haber puesto _lista_de_ordenes_
pero queremos resaltar que el código de retorno va a ser evaluado.
Las partes entre corchetes son opcionales y si van seguidas de tres puntos
'...' indica que puede presentarse varias veces.
Todas las sentencias condicionales
'if' empiezan con la palabra reservada 'if' y terminan con la palabra reservada
'fi'.
Mejor vamos a ilustrar con algunos ejemplos. Deberá editarlos y ejecutarlos.
Acabamos de utilizar una condición que siempre se cumplirá. Vamos
a ver algo un poco más util.
También podemos programar una acción para cuando se cumpla una condición
y otra para cuando no se cumpla.
El siguiente ejemplo editelo con el nombre 'tipofichero'. Nos servirá
para ilustrar el uso de 'elif' y para repasar algunas de las opciones
de 'test'.
Para usar este último ejemplo ejectute './tipofichero ..' o
'./tipofichero tipofichero'
Tenga en cuenta que si bien la 'bash' admite el uso de 'elif' quizas
otras shell no lo admitan.
Setencia condicional 'case'
Existe un caso especial. Imaginemos que deseamos evaluar
una variable y en función de lo que contenga hacer una cosa u otra.
Podríamos usar una sentencia condicional con abundantes 'elif' pero
hay que tener en cuenta una cosa. Con 'elif' tenemos que ir evaluando
a cada paso hasta que se cumpla una vez y entonces se ejecuta la acción
y ya no se ejecuta ninguna otra porque habremos finalizado. En el caso
anterior si hubieramos introducido el nombre de un pipe con nombre se
habrían ejecutado varios test antes de determinar que era un pipe con
nombre, y eso resulta lento.
Si lo que deseamos hacer está en función de un valor utilizaremos la
sentencia condicional 'case' que es mucho más eficiente porque no requiere
ejecutar varias ordenes o una orden varias veces como en el caso anterior.
La sintaxis para la sentencia condicional 'case' sería de la siguiente forma:
Ahora le damos permiso de ejecución y probamos como funciona.
Recuerde que la ejecución de una orden externa en un shell-srcipt es una
operación costosa. Hacer un script ineficiente puede no tener ninguna
importancia dependiendo del uso que le demos y de lo sobrada de recursos
que esté la máquina, pero en otros caso si será más importante así que
haciendo las cosas lo mejor posible estaremos preparados en un futuro
para trabajos más exigentes.
Entrada de datos con read
La instrucción 'read' permite capturar datos desde la entrada estandar.
Para ello queda a la espera de un fin de linea. Edite el fichero siguiente
y llameló 'pru_read'.
Ejecute './pru_read'. Observe que los datos introducidos son guardados
en la variable NOMBRE. Si pulsa
Cuando se usa la instrucción 'read' sin una variable el contenido se
guardará en la variable REPLY pero si lo que desea es guardar el contenido
queda más claro guardarlo en una variable concreta. Más adelante veremos
ejemplos que usan esta instrucción.
Bucles 'while' y 'until'
En lugar de _condición_ podríamos haber puesto _lista_de_ordenes_ pero queremos resaltar que el código de retorno va a ser evaluado.
Mientras se cumpla la condición se ejecutará _lista_de_ordenes_.
Resulta evidente que si dentro de la _lista_de_ordenes_ no hay nada
capaz de
alterar la condición, resultará un blucle que se ejecutará de forma
ininterrumpida. (Bucle infinito). Si esto ocurre habrá que matar el
proceso enviando alguna señal.
Existe un bucle muy similar y que solo se diferencia en que no
para nunca hasta que se cumpla la condición. Es decir justo al revés
que antes.
Cuando pulsemos <INTRO> directamente sin nada más LINEA valdrá "" y
'test $LINEA' devolverá FALSE y el bucle finalizará.
Vamos a ejecutar el ejemplo para comprobar su funcionamiento.
Vemos que en la última ejecución LINEA valía "".
Bucle 'for'
El siguiente ejemplo funciona pero es una mala idea ya que supone
Varias ejecuciones de un comando cuando se podía haber hecho con
uno solo. Habría sido mejor hacer 'chmod +x *'.
El siguiente ejemplo no funcionará si no prepara un poco una serie
de cosas que se necesitan. Concretamente deberá de existir un fichero
'lista' con varios elementos. Varios ficheros con nombre que empiecen
por SCAN. Varios ficheros '*.doc' de los cuales algunos deberan
contener la palabra 'CODIGO'. Estudie el contenido del ejemplo que sigue.
Editelo y ejecutelo pasando varios argumentos. Su objetivo es hacerlo
funcionar y entender como funciona.
Cuando tengamos bucles unos dentro de otros decimos que son bucles
anidados. El nivel de anidamiento de un bucle es el número de
bucles que hay unos dentro de otros.
'break' y 'continue'
La palabra reservada 'break' provoca la salida de un bucle por el final.
Si viene seguido de un numero 'n' saldrá de 'n' niveles. No poner número
equivale a poner el número 1.
La palabra reservada 'continue' provoca un salto al comienzo del bucle para continuar con la
siguiente iteración. Si viene seguida de un numero 'n' saldrá de 'n' niveles.
Probamos ahora a ejecutarlo y obtendremos
Ahora edite y pruebe el siguiente ejemplo que llamaremos 'pru_break_continue2'
El resultado de la ejecución sería como sigue.
Arreglos
El resultado de ejecutar esto introduciendo una serie de datos sería
como sigue:
Con esto comprobamos que podemos acceder no solo a la lista completa
de los datos introducidos sino a uno solo de ellos proporcionando un
número con la posición donde el dato ha sido almacenado.
También comprobamos que el valor 'jjjj' se ha indroducido varias veces
pero nuestro programa solo lo guarda una vez gracias a que antes de guardar
cada valor se comprueba si dicho valor ya fúe guardado antes.
Un ejempo sencillo: Construcción de un menu
Vamos a comentar algunos aspectos de este programa.
Comienza con 'while true' (Antes de esto lo que hay es la
declaración de un par de funciones). Un programa así se ejecutaría en
un bucle sin fin pero existe una instrucción 'exit' en medio del programa
para finalizar cuando se elija la opción adecuada.
Primero se llama a una función 'muestraopciones' que nos muestra las
opciones disponibles y nos invita a introducir una de ellas que gracias
a la instrucción read será almacenada en la variable OPT. El contenido
de esta variable se usa en un par de sentencias 'case'. La primera solo
considera si es alguna de las opciones '3,7, o 4' y en función de ello
muestra un mensaje adicional. El segundo 'case' sirve para desencadenar
la funcionalidad elegida. Existe un '*)' que sirve para advertir que la
opción introducida no es correcta. Quizas ya ha notado que en ocasiones
usamos la opción -e con echo. Esto se hace cuando usamos un caracter
especial que debe ser interpretado como en el caso de '\a' (emite un
pitido), o como '\c' (evita el salto de carro). El comando 'clear' se
usa para limpiar la pantalla.
Recomendaciones finales
En estos momentos ya dispone de toda la base de conocimientos que le
permitirá personalizar su entorno de trabajo o construir sencillos
scripts que le ayuden a realizar una gran variedad de tareas, o
también puede investigar algunos de los programas del sistema escritos
en shell-script. Si se conforma con lo estudiado y no intenta ponerlo
en práctica pronto lo olvidará todo. Practique la programación en
shell-script ya mismo antes de que olvide la tehoría. Empiece por las
cosas que hemos explicado pero no se quede en eso. Intente llegar un
poco más lejos.
En las lecciones que siguen continuaremos tratando temas que abrirán
nuevas puertas a su conocimiento. En ningún caso podemos detenernos
excesivamente en ninguna de ellas. A la programación en shell-script
la hemos dedicado varias lecciones y anteriormente dedicamos algunos
más a la shell como interprete de comandos, pero ha llegado el momento
de decir que debe de poner mucho de su parte y que existe el 'man bash'
para buscar las cosas que no pudimos explicar aquí.
No vamos a incluir ningún test ahora en lugar de ello y a riesgo de
resultar pesados vamos a insistir. Practique ahora por su cuenta.
Ya hemos visto una forma de hacerlo usando los operadore '||' o '&&'
de la shell, pero existen formas que pueden resultar más versatiles y
más legibles.
if _condición_
then _lista_de_ordenes_
[ elif _condición_
then _lista_de_ordenes_ ] ...
[ else _condición_ ]
fi
# Esto siempre mostrara '123'
if true
then echo '123'
fi
# Si la variable CMPRT01 está definida y contiene el nombre de un fichero
# con permiso de lectura se mandará a la impresora con 'lp'
if test -r $CMPRT01
then lp $CMPRT01
fi
# Si La variable 'modo' contiene el valor 'lp' imprimir el fichero $FICH
# en caso contrario sacarlo por pantalla.
if [ "$modo" = "lp" ]
then lp $FICH
else cat $FICH
fi
# tipofichero
FILE=$1
if test -b $FILE
then echo $FILE 'Es un dispositivo de bloques'
elif test -c $FILE
then echo $FILE 'Es un dispositivo especial de caracteres'
elif test -d $FILE
then echo $FILE 'Es un directorio'
elif test -f $FILE
then echo $FILE 'Es un fichero regular (ordinario)'
elif test -L $FILE
then echo $FILE 'Es un Link simbólico'
elif test -p $FILE
then echo $FILE 'Es un pipe con nombre'
elif test -S $FILE
then echo $FILE 'Es un Socket (dispositivo de comunicaciones)'
elif test -e $FILE
then echo $FILE 'Existe pero no sabemos que tipo de fichero es'
else echo $FILE 'No existe o no es accesible'
fi
Ya hemos visto que con 'elif' podemos evaluar distintas condiciones
programando una acción para cada caso.
case valor in
[ _lista_de_patrones_ ) _lista_de_ordenes_ ;; ] ...
esac
Por valor queremos indicar el resultado de cualquier expansión producida
por la shell. La _lista_de_patrones_ son uno o más patrones separados por
el caracter '|' y cada patrón es del mismo tipo que los patrones utilizados
para la expansión de nombres de ficheros. El orden es importante porque
se van comprobando por orden y en el momento que uno de los patrones
corresponda con el valor se ejecutará la _lista_de_ordenes_ que le
corresponda y ya no se ejecutará nada más. Es frecuente utilizar en
último lugar un patrón '*)' para que en caso de no encontrar ninguna
correspondencia con los patrones anteriores se ejecute siempre una
acción por defecto. Edite el siguiente ejemplo y salvelo con nombre
'prueba_case'.
# prueba_case
case $1 in
1) echo Uno ;;
2) echo Dos ;;
[3-7]) echo "Entre tres y siete ambos incluidos" ;;
8|9|0) echo Ocho; echo Nueve; echo Cero ;;
[a-zA-Z]) echo Una Letra ;;
start|begin) echo Comienzo ;;
stop|end) echo Fin ;;
*) echo 'Fallo'
esac
$ ./prueba_case 1
Uno
$ ./prueba_case 2
Dos
$ ./prueba_case 7
Entre tres y siete ambos incluidos
$ ./prueba_case h
Una Letra
$ ./prueba_case start
Comienzo
$ ./prueba_case begin
Comienzo
$ ./prueba_case aa
Fallo
$ ./prueba_case 12
Fallo
$ ./prueba_case 9
Ocho
Nueve
Cero
$ ./prueba_case stop
Fin
echo -e "Introduzca su nombre : \c"
read NOMBRE
banner Hola $NOMBRE
while _condición_
do
_lista_de_ordenes_
done
until _condición_
do
_lista_de_ordenes_
done
Edite como 'pru_while1' el siguiente ejemplo.
# pru_while1
# Hacer un bucle de captura por consola (terminara cuando se pulse
# solo <INTRO> ) y mostrar el resultado de cada captura entre parentesis.
# Inicializamos primero la variable LINEA con un valor cualquiera
# pero distinto de ""
LINEA="x"
while test $LINEA
do
read LINEA
echo '('$LINEA')'
done
$ ./pru_while1
aaaaa
(aaaaa)
bbbbb
(bbbbb)
()
Se proporciona una lista de elementos y se ejecuta una lista de órdenes
haciendo que una variable vaya tomando el valor de cada uno de
estos elementos. Entre estas ordenes se puede utilizar un nuevo
bucle 'for'.
for i in $*
do
chmod +x $i
done
# Para ver distintos modos de manejar listas vamos a generar todos los
# nombres posibles formados por combinacion de las partes siguientes:
# Como parte 1 los nombres de ficheros de directorio actual que
# empiezan por 'SCAN'.
# Como parte 2 los nombres contenidos en el fichero 'lista'.
# Como parte 3 los identificadores 'cxx0 cxx2 bxx5'
# Como parte 4 la lista de parametros $1 $2 $3 .. etc,
# Como parte 5 los ficheros '*.doc' que contienen la palabra 'CODIGO'.
###
for i in `ls SCAN*`
do
for j in `cat lista`
do
for k in cxx0 cxx2 bxx5
do
for l in $*
do
for m in `grep -l "CODIGO" *.doc`
do
echo $i.$j.$k.$l.$m
done
done
done
done
done
Existe una forma de controlar un blucle desde el interior del mismo.
Para eso podemos usar 'break', o 'continue'. Se pueden usar en
cualquiera de los bucles que acabamos de ver (while, until, y for).
# pru_break_continue
# Definir la variable j como una variable de tipo entero e
# inicializarla a cero. Luego la incrementamos a cada iteración
# del bucle y si j es menor que diez mostramos el doble de su valor.
# y en caso contrario salimos del bucle
typeset -i j=0
while true
do
let j=j+1
if [ $j -et 3 ]
then continue
fi
if [ $j -et 4 ]
then continue
fi
if [ $j -lt 10 ]
then expr $j \* 2
else break
fi
done
$ ./pru_break_continue
2
4
10
12
14
16
18
# ./pru_break_continue2
for i in uno dos tres
do
for j in a b c
do
for k in 1 2 3 4 5
do
echo $i $j $k
if [ "$j" = "b" ]
then break
fi
if [ "$k" = "2" ]
then continue 2
fi
done
done
done
$ ./pru_break_continue2
uno a 1
uno a 2
uno b 1
uno c 1
uno c 2
dos a 1
dos a 2
dos b 1
dos c 1
dos c 2
tres a 1
tres a 2
tres b 1
tres c 1
tres c 2
Vamos a estudiar ahora un tipo de variables que se caracterizan
porque permiten guardar valores asociados a una posición. Los
llamamos 'arreglos' (en ingles array) también se pueden llamar
tablas. En el siguiente ejemplo usaremos una varirable 'tab[]'
que se comporta como si estuviera formada por varias variables.
tab[1], tab[2], tab[3], etc...
Vamos a ilustrarlo con un ejemplo que tiene un poco de todo.
Se trata de un programa que debe estudiar detenidamente.
# Desarrollar una función que admita un parametro de entrada. Si el
# parametro de entrada contiene una cadena que ya esta almacenada en
# la tabla 'tabnom' retornar sin mas, pero si no esta añadir esa
# palabra a la tabla.
GuardaNombre(){
# Si numero de parametros distindo de 1 salir con error.
if [ $# -ne 1 ]
then
echo "Numero de parametros invalido en GuardaNombre()"
return 2
fi
typeset -i j=1
for var in ${tab[*]}
do
if [ $var = $1 ]
then
ind=$j
return 1
fi
let j=j+1
done
ind=$j
tab[$ind]=$1
return 0
}
########################## main ##################
while true
do
echo -e "Introduzca algo o puse <INTRO> directamente para finalizar : \c"
read DATO
if [ ! "$DATO" ]
then break
fi
GuardaNombre $DATO
done
echo "Ahora visualizamos los datos introducidos"
for l in ${tab[*]}
do
echo $l
done
echo 2 ${tab[2]}
echo 1 ${tab[1]}
Introduzca algo o puse <INTRO> directamente para finalizar : hhhhh
Introduzca algo o puse <INTRO> directamente para finalizar : jjjj
Introduzca algo o puse <INTRO> directamente para finalizar : jjjj
Introduzca algo o puse <INTRO> directamente para finalizar : jjjj
Introduzca algo o puse <INTRO> directamente para finalizar : ooooooooo
Introduzca algo o puse <INTRO> directamente para finalizar : kk
Introduzca algo o puse <INTRO> directamente para finalizar :
Ahora visualizamos los datos introducidos
hhhhh
jjjj
ooooooooo
kk
2 jjjj
1 hhhhh
#########################################################################
muestraopcionesmenuprin() {
clear
echo '1) Fecha y hora'
echo '2) Calendario del més actual'
echo '3) Calendario del año actual'
echo '4) Calculadora de precisión arbitraria'
echo '5) Lista de usuarios conectados'
echo '6) Memoria libre del sistema'
echo '7) Carga del sistema'
echo '8) Ocupacion de todo el sistema de ficheros'
echo '0) Terminar'
echo
echo -e "Introduzca la opción deseada : \c"
}
#########################################################################
pausa () {
echo
echo -e "Pulse
Acabamos de explicar un programa muy sencillo. Compliquelo un poco
añadiendo una opción 9 que muestre un submenú que entre otras cosas
contenga una opción que vuelva al menú principal. Use su imaginación
y parctique para intentar mejorarlo.
Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos