4 - La shell (2)


Curso gratis creado por Publispain . Extraido de: http://www.publispain.com
22 Agosto 2005
- 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 <comandos> <señales>" . 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 <comando>’ , 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 <código de retorno>.

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 <codigo de error>".

- 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

-Comando at <cuando> <comando a ejecutar>

Ejecuta , a la hora determinada , el <comando>. Puede ser una shell-script , ó un ejecutable.

Este comando admite la entrada desde un fichero ó bien desde el teclado. Normalmente , le daremos la entrada usando el "here document" de la shell.

El "cuando" admite formas complejas de tiempo. En vez de contar todas , veamos algunos ejemplos que seguro que alguno nos cuadrará :

* Ejecutar la shell "salu2.sh" que felicita a todos los usuarios , pasado mañana a las 4 de la tarde:

# at 4pm + 2 days <<EOF

/usr/yo/salu2.sh

EOF

* Lanzar ahora mismo un listado:

at now + 1 minute <<EOF

"lp -dlaserjet /tmp/balance.txt"

EOF

* Ejecutar una cadena de reindexado de ficheros larguísima y apagar la máquina:

at now + 3 hours <<EOF

"/usr/cadenas/reind.sh 1>/trazas 2>&1 ; shutdown -h now"

EOF

* A las 10 de la mañana del 28 de Diciembre mandar un saludo a todos los usuarios :

at 10am Dec 28 <<EOF

wall "Detectado un virus en este ordenador"

EOF

* A la una de la tarde mañana hacer una salvita :

at 1pm tomorrow <<EOF

/Tutoriales/Salvas/diario.sh

EOF

De la misma manera que el comando nohup , éstos comandos de ejecución en diferido mandan su salida al mail , por lo que hay que ser cuidadoso y redirigir su salida a ficheros personales de traza en evitación de satu rar el directorio /var/mail.

El comando "at" asigna un nombre a cada trabajo encolado , el cual lo podemos usar con opciones para consultar y borrar trabajos :

at -l : lista todos los trabajos en cola , hora y día de lanzamiento de los mismos y usuario.

at -d <trabajo>: borra <trabajo> de la cola.

Ya que el uso indiscriminado de éste comando es potencialmente peligroso , existen dos ficheros que son inspeccionados por el mismo para limitarlo : at.allow y at.deny. (Normalmente residen en /usr/spool/atjobs ó en /var/a t).

at.allow:si existe, solo los usuarios que esten aquí pueden ejecutar el comando "at".

at.deny:si existe, los usuarios que estén aquí no estan autorizados a ejecutar "at".

El "truco" que hay si se desea que todos puedan usar at sin tener que escribirlos en el fichero , es borrar at.allow y dejar sólo at.deny pero vacío.

- Batch .

Ejecuta el comando como en "at" , pero no se le asigna hora ; batch lo ejecutará cuando detecte que el sistema se halla lo suficientemente libre de tareas. En caso que no sea así , se esperará hasta que oc urra tal cosa.

-Cron.

No es un comando ; es un "demonio" , ó proceso que se arranca al encender la máquina y está permanentemente activo. Su misión es inspeccionar cada minuto los ficheros crontabs de los usuarios y ejec utar los comandos que allí se digan a los intervalos horarios que hayamos marcado. Como se acaba se señalar , los crontabs son dependientes de cada usuario.

Se deben seguir los siguientes pasos para modificar , añadir ó borrar un fichero crontab :

1 - Sacarlo a fichero usando el comando "crontab -l >/tmp/mio" , por ejemplo.

2 - Modificarlo con un editor de acuerdo a las instrucciones de formato que se explican a continuación.

3 - Registrar el nuevo fichero mediante el comando "crontab /tmp/mio" , por ejemplo.

mira cada minuto el directorio /var/spool/crontabs y ejecuta los comandos

crontab .

El formato del fichero es el siguiente :

#minutos horas dia mes mes dia-semana comando ( # = comentario )

minutos : de 0 a 59.

horas : de 0 a 23.

dia del mes : de 0 a 31.

mes : de 0 a 12.

día semana : de 0 a 6 ( 0 = domingo , 1 = lunes ...)

Aparte de la especificación normal , pueden utilizarse listas , es decir , rangos de valores de acuerdo con las siguientes reglas :

8-11 : Rango desde las 8 hasta las 11 ambas inclusive.

8,9,10,11 : Igual que antes ; desde las 8 hasta las 11.

Si queremos incrementar en saltos diferentes de la unidad , podemos escribir la barra "/" :

0-8/2 : Desde las 12 hasta las 8 cada 2 horas. Equivale a 0,2,4,6,8 .

El asterisco significa "todas" ó "todo". Por tanto :

*/2 : Cada dos horas.

Ejemplos más evidentes :

# ejecutar una salvita todos los dias 5 minutos despues de las 12 de la noche :

5 0 * * * /Tutoriales/salvas/diaria.sh 1>>/Tutoriales/salvas/log 2>&1

# ejecutar a las 2:15pm el primer dia de cada mes:

15 14 1 * * /Tutoriales/salvas/mensual.sh 1>/dev/null 2>&1

# ejecutar de lunes a viernes a las diez de la noche - apagar la maquina que es muy tarde

0 22 * * 1-5 shutdown -h now 1>/dev/null 2>&1

# ejecutar algo cada minuto

* * * * * /Tutoriales/cosas/espia.sh

4 TRATAMIENTO DE FICHEROS.

Comando :wc <opciones> <nombre-archivo>

Misión: cuenta lineas,palabras o caracteres de un fichero.

opciones:

-l:lineas

-c:letras

-w:palabras

Ejemplo:

l=`wc -l /etc/passwd` ; echo "Hay $l usuarios registrados en este equipo"

En la variable $l tendríamos el número de líneas del fichero /etc/passwd.

Comando: tr <opciones> 'caracteres orig' 'caracteres destino' < nombre-archivo

Misión: sustituye globalmente un caracter por otro .

Ejemplo:

tr '[a-z]' '[A-Z]' < /etc/passwd >/tmp/passwd.MAYUSCULAS

Pasa el fichero de entrada a mayúsculas.

Comando: cut <opciones> <nombre de archivo>

Misión: corta texto a través del número absoluto de columna o el número relativo de

campo.

Opciones:

-c:numero absoluto de columna

-f:numero de campo.

-d:delimitador de campo.

Especificación de números de columna.

1,3,10 : primera , tercera y decima columnas.

1-3 : de la primera a la tercera columna.

1-3,8 : de la primera a la tercera y la octava.

-5 : de la primera a la quinta columna

5- : de la quinta a la última.

Ejemplo :

cut -d"." -f1 /etc/passwd

lista sólamente los nombres del fichero /etc/passwd , cortando por el delimitador ‘:’ .

Comando: paste.

Misión:

Comando : split <-numero de lineas de los ficheros> <fichero>

Misión: divide <fichero> en n ficheros de <num.lin> cada uno. Si no se indica el

numero de lineas , se consideran 1000.

los ficheros que genera se llaman xaa , xab , xac ...

Ejemplo:

$ split gordo

$ ls

gordo xaa xab xac xad xae xaf xag

Ha partido el fichero "gordo" en ficheros más pequeños , de 1000 líneas cada uno , denominados "xaa" ,"xab" ..... Si borramos el fichero "gordo" , podríamos volver a ge nerarlo con el comando siguiente:

$ cat xaa xab xac xae xaf xag > gordo

Comando: sort <opciones> <indicador de campo> <fichero>

Misión: ordena un fichero de acuerdo a una secuencia de odenacion determinada.

Ejemplo

dado un fichero con:

codigo nombre ciudad telefono

$ sort fichero # ordena por todos los campos

$ sort +1 fichero # ordena a partir del campo 1 (nombre ; codigo es el 0)

$ sort +2n fichero # ordena por nombre , en secuencia invertida.

$ sort +2n -t":" fichero # igual que antes , pero suponiendo que el separador es ":".

Comando: grep <cadena a buscar> <fichero>

Misión: buscar apariciones de palabras ó textos en ficheros.

Ejemplo:

$ grep root /etc/passwd # busca las líneas que contienen "root" en el fichero.

root:Ajiou42s:0:1:/:/bin/sh

Otro:

$ ps -e | grep -v constty # busca qué procesos no ruedan en la consola

( ...... )

La familia de grep , así como otros comandos tales como de (editor de líneas), sed (editor batch) y vi , manejan expresiones regulares para la búsqueda de secuencias de caracteres. Una expresión regular es un a expresión que especifica una secuencia de caracteres. Un ejemplo de ésto es el comando : ls -l | grep "^d" ; el "^d" es una expresión regular que equivale a decir "si la letra -d- está al principio de la línea" ; la construcción anterior nos lista sólo los directorios.

Las expresiones regulares conviene esconderlas de la shell encerrándolas entre comillas.

Algunos montajes de expresiones regulares son :

<.> (Punto) :cualquier carácter distinto al del fin de línea.

[abc] :la letra a , la b ó la c.

[^abc] :cualquier letra distinta a a , b ó c.

[a-z] :cualquier letra de la -a- a la -z- .

Una letra seguida de un asterisco -*- equivale a cero ó más apariciones de la letra. Por tanto , la expresión .* equivale a decir "cualquier cosa". Por tanto, el comando :

ls -l | grep ‘\.sh.*"

lista todos los ficheros que terminen en .sh mas los terminados en .sh<otras letras> . Nótese que el punto inicial lo hemos desprovisto de su significado "escapándolo" con un backslash -\- .

^ : al principio de una expresión regular equivale a "desde el principio de la línea".

$ : al final de una expresión regular equivale a "hasta el final de la línea".

Ejemplos de ésto :

ls -l | grep ‘\.sh$’ : saca los ficheros que SOLO acaben en .sh

Este tema se complica todo lo que se quiera . Veamos algunas expresiones regulares bastante usadas en comandos tipo sed ( que veremos más tarde ) para percatarse de ello :

[^ ] : cualquier letra diferente de espacio.

[^ ]* : una palabra cualquiera (varias letras no blancas).

[^ ]* * : una palabra seguida de un número incierto de blancos.

^[^ ]* * : la primera palabra de la línea y a todos los blancos que la siguen.

Comando: diff <fichero1> <fichero2>

Misión: Encuentra las diferencias entre dos ficheros.

Ejemplo:

$ cat nombres1

jose

juan

roberto

$ cat nombres2

jose

roberto

$ diff nombres1 nombres2

2d1

< juan

Comando: join <fichero1> <fichero2>

Misión: mezcla ficheros clasificados

$ cat uno

antonio alperchines,5

juan seneca,32

maria oxigeno,45

pablo isaac peral,54

$ cat dos

antonio madrid

juan segovia

maria avila

pablo guadalajara

$ join uno dos

antonio alperchines,5 madrid

juan seneca,32 segovia

maria oxigeno,45 avila

pablo isaac peral,54 guadalajara

Comando: fold <-ancho-en-columnas> <fichero>

Misión: pasar palabras automáticamente a la línea siguiente. En el caso de tener un texto con líneas de más de 80 caracteres , al imprimirlo queda truncado. Usando éste comando , cualquie r texto que sobrepase el ancho establecido se pasa a la línea inferior.

Ejemplo:

fold -80 carta.txt | lp

Comando: sed ‘comandos de sed’ <ficheros>

Misión efectúa cambios en ‘fichero’ , enviando el resultado por la salida estándar. Pueden guardarse los resultados de éstas operaciones en un fichero como siempre , redireccionando la salida , tal que - sed ‘expresion’ fichero >/tmp/salida -.

Utilizaremos sed normalmente cuando sea necesario hacer cambios en ficheros de texto ; por ejemplo , nos puede valer para cambiar determinada variable en una serie de ficheros de profile de usuario , etc.

La explicación completa de éste comando exigiría mucho detalle ; veamos sólamente algunos ejemplos de operaciones con sed :

* Cambiar , en el fichero /Tutoriales/pepito/.profile , la expresión TERM=vt100 por TERM=vt220 . Usamos el comando ‘s/expresion_a_buscar/expresion_a_cambiar/g’ (s=search,g=global) :

sed ‘s/TERM=vt100/TERM=vt220/g’ /Tutoriales/pepito/.profile >/tmp/j && mv /tmp/j /Tutoriales/pepito/.profile

(si el comando ha sido OK , entonces movemos el /tmp/j).

* Meter tres blancos delante de todas las líneas del fichero /tmp/script.sh :

sed ‘s/^/ /’ /tmp/script.sh >/tmp/j && mv /tmp/j /tmp/script.sh

(el ^ identifica el principio de cada línea).

* (mas raro): Cepillarse todos los ficheros de un directorio :

ls | sed ‘s/^/rm -f /’ | sh

(del ls sale el fichero ; el sed le pone delante "rm -f" y el sh lo ejecuta.

Autor y licencia de 'Curso de Unix de la A a la Z'


Curso gratis de Publispain . Extraido de: http://www.publispain.com CopyLeft
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.