Página principal

Introduccion. 1 Antecedentes históricos


Descargar 277.59 Kb.
Página3/4
Fecha de conversión18.07.2016
Tamaño277.59 Kb.
1   2   3   4

- Comillas simples ‘ ’ :Quitan el significado especial de todos los caracteres encerrados entre comillas simples.

- Comillas dobles " " :Quitan el significado especial de todos los caracteres EXCEPTO los siguientes : $ (dolar) , \ (backslash) y ` (comilla de ejecución).

- Comillas de ejecución ` ` :Ejecutan el comando encerrado entre las mismas y sustituyen su valor por la salida estándar del comando que se ha ejecutado .

Es mejor , sobre todo en el último caso , ver algunos ejemplos :

- Para sacar un cartel en pantalla que contenga comillas , deberemos "escaparlas" pues , si no , la shell las interpretaría , como en :

# echo "Pulse "INTRO" para seguir" ( MAL!! - la shell ve 4 comillas y no las sacaría ! )

# echo "Pulse \"INTRO\" para seguir" ( AHORA SI sale bien )

- También , podríamos haber escrito el texto entre comillas simples :

# echo ‘Pulse "INTRO" para seguir’ ( BIEN como antes)

lo que ocurre es que de ésta manera no se interpretaría nada ; nos podría convenir algo como:

# echo ‘Oye , $LOGNAME pulsa "INTRO" para seguir’

y saldría :

Oye , $LOGNAME pulsa INTRO para seguir

Lo cual no vale. Habría que poner :

# echo "Oye , $LOGNAME pulsa \"INTRO\" para seguir"

y saldría :

Oye , root pulsa INTRO para seguir.

- En el caso de comillas de ejecución , podemos escribir :

# echo "Oye , `logname` pulsa \"INTRO\" para seguir"

(sale bien , la shell sustituye el comando logname por su resultado)

o bien , valdrían expresiones como:

# echo "Y estas en el terminal : `tty`"

Y estas en el terminal /dev/ttyp002

Hay que imaginarse , por tanto , que la shell "ve" el resultado del comando en la línea de ejecución.

Valen también , como es lógico , asignaciones a variables :

# TERMINAL=`tty`

# echo $TERMINAL

/dev/ttyp001

3.7 Programación con shell.scripts.

La shell , además de interpretar y ejecutar comandos , tiene primitivas de control de ejecución de programas tales como sentencias condicionales y bucles.

La interpretación del lenguaje se lleva a cabo prácticamente en tiempo real ; a medida que va interpretando va ejecutando.

Los programas , como se ha mencionado antes , se interpretan en tiempo de ejecución. Por tanto , la codificación de una shell-script es sumamente sencilla en el sentido en el que basta con escribir en un fichero de texto l os comandos y ejecutarlo.

- Variables.

Dentro de una shell , existen las variables de entorno que hayamos definido anteriormente , bien en la misma , en otra ó en los ficheros profile de inicialización. Además de éstas , existen otras que detallam os a continuación :

$0 : Su contenido es el nombre de la shell-script que estamos ejecutando.

$1 , $2 : Primer y segundo parámetro posicional.

$# : Número de parámetros que han pasado a la shell.

$* : Un argumento que contiene todos los parámetros que se han pasado ( $1 , $2 ...) menos el $0.

$? :Número donde se almacena el código de error del último comando que se ha ejecutado.

$$ :Número de proceso actual (PID)

$! :Ultimo número de proceso ejecutado.

# :COMENTARIO : Todo lo que haya a la derecha de la almohadilla se toma como comentario.

Ejemplo : Supongamos que hemos escrito la siguiente shell-script llamada "prueba.sh" :

echo "La escript se llama $0"

echo "Me han llamado con $# argumentos"

echo "El primero es $1"

echo "Y todos son $*"

echo "Hasta luego lucas!"

Y la podemos ejecutar de dos maneras :

1) Directamente :

# sh prueba.sh uno dos

2) Dando permisos y ejecutando como un comando:

# chmod 777 prueba.sh

# prueba.sh uno dos

La salida :

Me han llamado com 2 argumentos

El primero es uno

Y todos son uno dos

Hasta luego lucas

Hemos visto que los comandos se separan por líneas , y se van ejecutando de forma secuencial. Podemos , no obstante , poner varios comandos en la misma línea separandolos por punto y coma ‘;’.

Además , podemos agrupar comandos mediante paréntesis , lo cual permite ejecutarlos en un subentorno ( las variables que usemos no nos van a interferir en nuestro proceso "padre" )

# ( date ; who ) | wc -l

Normalmente , ejecutar una shell implica crear un proceso hijo , y el proceso padre (normalmente , nuestra sesión inicial de shell) espera a que dicho proceso acabe para continuar su ejecución ( si nosotros lanzamos un pro grama shell-script , hasta que éste no acaba (hijo) , no nos aparece en pantalla el inductor de comandos ‘#’ (padre) ).

Por definición , en UNIX un proceso hijo , al rodar en un espacio de datos distinto , hereda varias cosas del padre , entre ellas todas las variables de entorno ; pero por ello , no puede modificar el entorno del padre ( si modif icamos en una shell script el contenido , por ejemplo , de "TERM" , al acabar dicha shell y volver al padre la variable continúa con su valor original. Hay situaciones en las cuales necesitamos que una shell modifique nuestro entorno actu al , y a tal efecto se ejecuta con un punto (.) delante de la shell-script.

Es mejor ver éste último aspecto mediante un programa en shell.script : supongamos una shell que se llame "tipoterm" que nos pida el terminal y nos ponga la variable TERM de acuerdo a ésta entrada :

# script para pedir el tipo de terminal

echo "Por favor escriba que terminal tiene :"

read TERM

echo "Ha elegido --- $TERM"

Si la ejecutamos como

# tipoterm

al volver al ‘#’ NO se ha modificado la variable! Para que SI la modifique , se llamaría como:

# . tipoterm

Hay que suponerse al punto como un "include" , que en vez de crear un proceso hijo "expande" el código dentro de nuestra shell actual.

- Comando read

Con el fin de permitir una ejecución interactiva , existe el comando "read " , el cual , en el momento de ejecución , espera una entrada de datos por teclado terminada en ; lo que han introducido por el teclado va a la variable especificada.

Supongamos la siguiente shell-script :

echo "Como te llamas?"

read nom

echo "Hola $nom"

Ejecución :

Como te llamas ?

jose ( aquí escribimos "jose" y pulsamos )

Hola jose

el comando "read" , ha cargado "jose" en "nom".

- Secuencias condicionales : if .. fi :

La sintaxis de ésta sentencia es :

if

then

..... comandos ....



else

..... comandos ....

fi

(la cláusula "else" puede omitirse ; sólo se usará cuando se requiera).



La condición puede escribirse como "test " ó con corchetes. Es imprescindible en este último caso , poner espacios entre los corchetes y los valores.

Posibles condiciones y su sintaxis :

if [ = ] : variable es igual a valor. Ojo con los espacios en ‘=‘ .

if [ != ] : variable es distinta a valor.

if [ ] : variable es igual a valor . La variable debe contener números. En éste caso , valen las comparaciones siguientes :

-eq : Igual (equal)

-ne : Distinto (not equal)

-ge : Mayor ó igual (Greater or equal).

-le : Menor ó igual (Less or equal).

-lt : Menor que (Less than).

-gt : Mayor que (Greater than).

if [ -f ] : Existe . Ojo con los espacios.

if [ -d ] : Existe y es un directorio.

if [ -s ] :Existe y tiene un tamaño mayor de cero.

if [ -x ] : Existe y es ejecutable.

( Hay mas , pero con éstos de momento es suficiente ).

En el campo vale escribir comandos , los cuales se ejecutarán y el valor de la condición dependerá de dos factores :

* Retorno 0 del comando = VERDADERO.

* Retorno != 0 del comando = FALSO.

Ejemplo de ésto último sería el siguiente programa :

if grep jose /etc/passwd

then # retorno del comando -grep- ha sido CERO

echo "Jose esta registrado como usuario"

else # retorno del comando grep NO ha sido CERO.

echo "Jose NO esta registrado como usuario"

fi

- Secuencia condicional case .. esac.



Sintaxis :

case in



) ( la variable es = valor , ejecuta los comandos hasta ‘;;’ )

;;

)



;;

* ) ( Clausula "otherwise" ó "default" : Si no se cumple alguna



de las anteriores ejecuta los comandos hasta ‘;;’ )

;;

esac ( Igual que if acaba en fi , case acaba en esac )



Ejemplos: minimenu.sh

clear # borrar pantalla

echo "1.- Quien hay por ahi ?" # pintar opciones

echo "2.- Cuanto disco queda ?"

echo "3.- Nada. Salir. "

echo "Cual quieres ? : \c" # el carácter "\c" evita que el echo salte nueva línea

read opcion # "opcion" vale lo que se ha tecleado en pantalla

case "$opcion" in # IMPORTANTE : Poner la variable como "$opcion"

1) who ;; # pues si el señor pulsa daría error al no valer nada.

2) df;;


3) echo "Adios";;

*) echo "Opcion $opcion Es Incorrecta" ;;

esac

- Bucles FOR.



Sintaxis :

for in

do

<.. comandos ..>

done


El bloque entre "for" y "done" da tantas vueltas como elementos existan en , tomando la variable cada uno de los elementos de para cada iteración . En ésto conviene no confun dirlo con los for..next existentes en los lenguajes de tipo algol (pascual , basic ...) que varían contadores .

Supongamos un programa que contenga un bucle for de la siguiente manera :

for j in rosa antonio

do

echo "Variable = $j"



done

Y la salida que produce es :

Variable es rosa

Variable es antonio

Explicación : el bloque ha efectuado dos iteraciones (dos vueltas). Para la primera , la variable -j- toma el valor del primer elemento -rosa- , y para la segunda , -antonio-.

En el campo podemos sustituir la lista por patrones de ficheros para la shell , la cual expande dichos patrones por los ficheros correspondientes ; de tal forma que al escribir

for j in *

la shell cambia el ‘*’ por todos los ficheros del directorio actual. Por tanto , el siguiente programa :

for j in *

do

echo $j



done

equivale al comando ‘ls’ sin opciones - merece la pena detenerse un momento para comprender ésto.

Vale tambien poner en el campo comillas de ejecución junto con cualquier comando ; la construcción - for j in `cat /etc/passwd` -, por ejemplo , ejecutaría tantas iteraciones como líneas tuviese dicho fichero , y para cada vuelta , la variable -j- contendría cada una de las líneas del mismo. Por tanto , valdrían expresiones como - for j in `who` - para procesar todos los usuarios activos en el sistema , - for j in `lpstat -o ` - , para procesar todos los listados pendientes , ó - for j in `ps -e` - para tratar todos los procesos de nuestra sesión.

- Bucles WHILE.

Sintaxis :

while

do

( ... comandos ... )



done

Aquí las iteraciones se producen mientras que la sea verdadera ( ó retorno = 0 ). En caso que sea falsa ( ó retorno != 0 ) , el bucle termina.

La sintaxis de es igual que en el comando -if- .

Ejemplo :

while [ "$opcion" != "3" ]

do

echo "Meta opcion"



read opcion

done


ó también , utilizando comandos :

echo "Escribe cosas y pulsa ^D para terminar"

while read cosa

do

echo $cosa >> /tmp/borrame



done

echo "Las lineas que has escrito son :"

cat /tmp/borrame

Explicación : El comando -read- devuelve un retorno VERDADERO (cero) mientras que no se pulse el carácter EOF ( ^D) ; por tanto , el bucle está indefinidamente dando vueltas hasta dar ése carácter.

- Contadores : sentencia expr.

La sentencia expr evalúa una expresión y la muestra en la salida estándar. La expresión normalmente consiste de dos números ó variables de contenido numérico y un operador de suma , resta , multiplicación ó división.

Son válidos los comandos siguientes:

expr 100 "+" 1 # saca en pantalla 101

expr 100 "-" 1 # saca en pantalla 99

expr 100 "*" 2 # OJO CON LAS COMILLAS DOBLES- Previenen a la shell de sustituciones.

# Bueno , todo el mundo sabe lo que es 100 * 2 , no?.

expr 100 "/" 2

Por tanto , podemos escribir :

pepe=`expr 10 "*" 5` # y la variable pepe vale 50.

ó incluso :

pepe=0


pepe=`expr $pepe "+" 1`

Esto último es bastante menos evidente . Para comprenderlo , hay que creerse que la shell ejecuta lo siguiente :

- Al principio , $pepe vale 0.

- En cualquier expresión de asignación , PRIMERO se calcula el resultado y DESPUES se ejecuta la asignación. Por tanto, lo primero que la shell hace es "expr 0 + 1".

- El "1" resultante va a sacar por la salida estándar. Pero como hemos puesto las comillas de ejecución , se asigna a pepe.

- Al final , $pepe vale 1.

Pues ya se pueden ejecutar bucles con contadores. Considerese el siguiente programa :

cnt=0


while [ $cnt -lt 50 ]

do

cnt=`expr $cnt "+" 1`



echo "Vuelta numero $cnt"

done


Se autoexplica.

- Operadores AND / OR.

Una construcción usual en la shell , utilizada principalmente por lo compacto de su código , pero con el inconveniente de que permite oscurecer el código son el operador "OR" -||- y el operador "AND&q uot; -&&- .

El operador "OR" ejecuta el primer comando , y si el código de error del mismo es FALSO (distinto de cero) , ejecuta el segundo comando.

El operador "AND" ejecuta el primer comando , y si el código de error del mismo es VERDADERO (igual a cero) , ejecuta el segundo comando.

Veamos un ejemplo y , por favor , reléase a continuación los dos párrafos anteriores :

cd /home/basura && rm -f *

Explicación : nos cambiamos al directorio indicado. Sólamente en el caso de haber tenido éxito , nos cargamos todos los ficheros del directorio actual.

ls /home/basurilla || mkdir /home/basurilla

Explicación : El comando ls falla si no existe el directorio indicado . En tal caso , se crea.

banner "hola" | lp && echo "Listado Mandado" || echo "Listado ha cascado"

Explicación : El comando son dos , el banner y el lp . Si por lo que sea no se puede imprimir , da el mensaje de error ; si va todo bien, sale Listado Mandado .

- Depuración de "shell scripts".

Si bien los métodos utilizados para ésto son bastante "toscos" , ha de tenerse en cuenta que la shell NO se hizo como un lenguaje de programación de propósito general . Cuando se requieren virguer&i acute;as , ha de acudirse bien a lenguajes convencionales ó bien a intérpretes más modernos y sofisticados (y más complicados , por supuesto) , tales como el TCL (Task Control Language) ó el PERL , los cuales si bien son de libre dominio no vienen "de fabrica" en todos los equipos.

Normalmente , emplearemos el comando "set" , el cual modifica algunos de los comportamientos de la shell a la hora de interpretar los comandos :

set -v : Verbose . Saca en la salida de error su entrada (es decir , las líneas del script según los va leyendo , que no ejecutando, pues pirmero se lee y después se ejecuta , que ésto es un intérpr ete y no hay que olvidarlo).

set -x :Xtrace . Muestra cada comando según lo va ejecutando por la salida de error , antecedido por un "+".

set -n :Noexec . Lee comandos , los interpreta pero NO los ejecuta. Vale para ver errores de sintaxis antes de probarlo de verdad.

set -e :Errexit. Terminar la ejecución inmediatamente si alguno de los comandos empleados devuelve un retorno distinto de VERDADERO ( 0 ) y NO se evalúa su retorno . El retorno de un comando se determina evaluado en las siguientes sentencias :

if..fi , while do..done , until do..done.

a la izquierda del operador AND/OR (-||- ó -&&- ).

- Comandos trap/exec/exit.

Como se vió en un capítulo anterior , cualquier proceso UNIX es susceptible de recibir señales a lo largo de su tiempo de ejecución. Por ejemplo , si el sistema se apaga (shutdown) , todos los procesos recibe n de entrada una señal 15 (SIGTERM) , y , al rato , una señal 9 .Por ahora , recordaremos que sólamente la señal 9 (SIGKILL) no puede ser ignorada ni redirigida . Un programa puede cambiar el tratamiento que los procesos hacen respecto de las señales , que suele ser terminar.

Las shell-scripts pueden efectuar cosas dependiendo de la señal que reciban , usando el comando "trap " . Este comando se suele poner al principio de la shell para que siga vigente a lo largo de toda la ejecución.

Así , podemos directamente ignorar una señal escribiendo lo siguiente :

trap "" 15

(Si llega la señal 15 (SIGTERM) , no hagas nada)

O evitar que nos pulsen Control-C , y si lo hacen , se acabó :

trap ‘echo "Hasta luego lucas" ; exit 1’ 2 3 15

(Si llega cualquiera , sacar el mensajito y terminar la ejecución con código de retorno 1).

Ojo con la ejecución de subshells - las señales ignoradas (las del trap ‘’) se heredan pero las demás vuelven a su acción original.

El comando ‘exec ’ , aparte de una serie de lindezas sobre redireccionamientos cuyo ámbito queda fuera de éste manual , reemplaza el proceso de la shell en cuestión con el del comando , el cual debe s er un programa ó otra shell-script. Las implicaciones que ésto tiene son que no se vuelve de dicha ejecución. Veamoslo , para no perder la costumbre , con un ejemplo :

Caso 1:

echo "Ejecuto el comando ls"



ls

echo "Estamos de vuelta"

Caso 2:

echo "Ejecuto el comando ls"



exec ls

echo "Estamos de vuelta"

En el caso -1- , la shell actual ejecuta un hijo que es el comando "ls", espera a que termine y vuelve , es decir , sigue ejecutando el echo "Estamos de vuelta". Sin embargo , en el caso -2- ésto no es as&iacu te; ; hacer exec implica que el número de proceso de nuestra shell pasa a ser el del comando "ls" , con lo que no hay regreso posible , y por tanto , el echo "estamos de vuelta" , NUNCA podría ejecutarse.

Por tanto , al ejecutar un nuevo programa desde exec , el número de proceso (PID) no varía al pasar de un proceso a otro.

-Comando exit .

Como ya hemos visto parcialmente , éste comando efectúa dos acciones:

Termina de inmediato la ejecución del shell-script , regresando al programa padre (que lógicamente podría ser otra shell ó directamente el inductor de comandos).

Devuelve al proceso antes citado un código de retorno , que podremos averiguar mirando la variable -$?- .

Al terminar una shell script , el proceso inmediatamente antes de acabar , recibe una señal 0 , útil en ocasiones para ver por dónde hemos salido usando el comando "trap".

- Funciones.

De manera similar a las utilizadas en lenguajes convencionales , dentro de una shell se pueden especificar funciones y pasarle parámetros. La sintaxis de una función sería :

nombre_funcion()

{

... comandos ...



}

Las variables de entorno de la función son las mismas que las de la propia shell-script ; debemos imaginar que la propia función se halla "incluída" en la porción de código desde donde la inv ocamos . Por tanto , una variable definida en una función queda definida en la shell y viceversa .

La excepción se produce con los parámetros posiciones ; el $1 , $2 .... cambian su sentido dentro de las funciones , en las cuales representan los parámetros con los que se la ha llamado.

Veamos para variar un ejemplo :

# programa de prueba - llamar con parametros

echo "Hola $1"

pinta()

{

echo "Hola en la función $1"



}

pinta "gorgorito"

Veamos el resultado de la ejecución :

# sh prueba "probando"

Hola probando

Hola en la funcion gorgorito

La variable -$1- , dentro del programa toma el valor del primer parámetro (probando) , y dentro de la función , el parámetro que se le ha pasado a la función (gorgorito). Una vez la función termina , e l $1 vale lo mismo que al principio.

La función nos puede devolver códigos de retorno utilizando la cláusula "return ".

- Ejecución en segundo plano : & , wait y nohup.

Si un comando en la shell termina en un umpresand -&- , la shell ejecuta el comando de manera asíncrona , es decir , no espera a que el comando termine .

La sintaxis para ejecutar un comando en segundo plano es :

comando &

Y la shell muestra un numerito por la pantalla , indicativo del número del proceso que se la lanzado. Hay que tener en cuenta que ése proceso es hijo del grupo de procesos actual asociado a nuestro terminal ; significa que si apagamos el terminal ó terminamos la sesión , tal proceso se cortará.

Para esperar a los procesos en segundo plano , empleamos el comando "wait" que hace que la shell espere a que todos sus procesos secundarios terminen.

Siguiendo con lo de antes , hay veces que ejecutamos :

# comando_lentisimo_y_pesadisimo &

Y queremos apagar el terminal e irnos a casa a dormir ; a tal efecto , existe el comando "nohup" (traducción : no cuelgues , es decir , aunque hagas exit ó apagues el terminal , sigue) , que independiza el proces o en segundo plano del grupo de procesos actual con terminal asociado.

Lo que ésto último quiere decir es que UNIX se encarga de ejecutar en otro plano el comando y nosotros quedamos libres de hacer lo que queramos.

Una duda : al ejecutar un comando en background , la salida del programa nos sigue apareciendo en nuestra pantalla , pero si el comando nohup lo independiza , que pasa con la salida ? La respuesta es que dicho comando crea un fichero ll amado "nohup.out" en el directorio desde donde se ha lanzado , que coniene toda la salida , tanto normal como de error del comando.

Ejemplo sobre cómo lanzar comando_lentisimo_y_pesadisimo :

# nohup comando_lentisimo_y_pesadisimo &

Sending output to nohup.out

12345

#

El PID es 12345 , y la salida del comando la tendremos en el fichero "nohup.out" , el cual es acumlativo ; si lanzamos dos veces el nohup , tendremos dos salidas en el fichero.



- Comandos a ejecutar en diferido : at , batch y cron.

Estos tres comandos ejecutan comandos en diferido con las siguientes diferencias :

AT lanza comandos una sola vez a una determinada hora.

BATCH lanza comandos una sola vez en el momento de llamarlo.

CRON lanza comandos varias veces a unas determinadas horas , días ó meses.

Estos comandos conviene tenerlos muy en cuenta fundamentalmente cuando es necesario ejecutar regularmente tareas de administración ó de operación. Ejemplos de situaciones donde éstos comandos nos pueden ayuda r son :

- Necesitamos hacer una salva en cinta de determinados ficheros todos los días a las diez de la mañana y los viernes una total a las 10 de la noche = CRON.

- Necesitamos dejar rodando hoy una reconstrucción de ficheros y apagar la máquina cuando termine ( sobre las 3 de la mañana ) , pero nos queremos ir a casa (son ya las 8) =AT

- Necesitamos lanzar una cadena de actualización , pero estan todos los usuarios sacando listados a la vez y la máquina está tumbada = BATCH

1   2   3   4


La base de datos está protegida por derechos de autor ©espanito.com 2016
enviar mensaje