P1N6Ü1N0 - SHELL - Introducción |
|
![]() Inicio C Perl Caml Shell GTK SQL |
Introducción al Shell Scripting Para entender los conceptos que se van a explicar aquí, es áltamente recomendable conocer algún lenguaje de programación, o al menos, tener unas nociones básicas sobre la programación imperativa en general. En primer lugar, empezaré explicando los comandos shell básicos para hacer control de flujo y cómo funcionan. Comenzaremos por ver los comandos builtin, que son los que vienen incluidos en el propio shell, es decir, que no son programas ejecutables. Pero antes, es imprescindible dar unas nociones básicas sobre la lógica en shell scripts, y las variables: Generalmente, cuando un programa termina correctamente, devuelve un 0 al sistema operativo, y cuando termina mal, cualquier otro valor. Existe un programa que siempre devuelve 0, que se llama true, y un programa que nunca devuelve cero, que se llama false. En cuanto a las variables, quien no oyó hablar de las variables de entorno alguna vez? Pues estas son las que se utilizan en los scripts. Basta con saber que se asignan haciendo: Por ejemplo, para añadir un directorio al path del sistema, podemos hacerlo
de la siguiente forma: No es muy interesante para scripts, pero conviene saber que asignando las variables de esa forma, los programas que se ejecuten desde nuestro script no podrán acceder a estos valores que acabamos de asignar a las variables. Para que puedan hacerlo, hay que exportarlas. Esto se hace mediante el comando export. Por ejemplo, es muy comun utilizarlo para ejecutar aplicaciones X-Window: export DISPLAY=mi_maquina:0.0 Visto esto, ya se puede empezar a ver los comandos disponibles para realizar el control de flujo en los scripts. IF El comando if nos permite realizar unas cosas u otras en función de los valores que devuelven los comandos al sistema.Por tanto, el comando if nos permite comprobar este valod devuelto al sistema por el comando. La sintaxis de if es: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi Podemos cambiar los ';' por retornos de carro. El funcionamiento es el siguiente: Se ejecuta COMMANDS, si el valor devuelto al sistema es 0, se ejecutan los COMMANDS del then, si no, los del else. fi simplemente indica cuando termina el comando if. elif funciona igual que si pusiéramos else if ..., pero nos ahorra cerrar el segundo if. Por tanto, un ejemplo del uso de if será: if ls archivo.c; then echo "Si existe";else echo "No existe";fi Esto puede ponerse también de la siguiente forma: if ls archivo.c then echo "Si existe" else echo "No existe" fi En este ejemplo hacemos uso de otro comando, el comando echo, este es en general un ejecutable del sistema. Por ahora basta con decir que muestra textos en pantalla. En este ejemplo, además, vemos que cuando el archivo no existe, sale un mensaje del comando ls por pantalla. Más adelante veremos cómo suprimir dicho mensaje. WHILE Ejecuta uno o mas comandos mientras el valor de salida de otro comando sea cero. Sintaxis: while COMMANDS; do COMMANDS; done Este comando es bastante sencillo de comprender, veamos un ejemplo: while true;do echo "repetimos";done Este ejemplo escribe todo el rato la palabra repetimos en pantalla. FOR Es un comando bastante potente, muchas de las cosas que haremos con scripts harán uso de él, porque es el típico comando para realizar el mismo procesamiento sobre muchos archivos distintos. Sintaxis: for NAME [in WORDS ... ;] do COMMANDS; done Para cada una de las palabras (WORDS), ejecuta el/los comando/s COMMANDS. Aquí teneis un ejemplo: for variable in uno dos tres cuatro cinco do echo $variable done Este ejemplo muestra en pantalla las palabras que ponemos despues de in. El comando for no suele utilizarse de esa forma, generalmente la lista de palabras que van despues de in, es generada por un procesamiento anterior, pero es aun pronto para adelantar esto. Con lo visto hasta aquí poco podemos hacer en cuanto a scripts, por eso
vamos a empezar con cosas algo más complejas que son las que dan a los
scripts toda su potencia.
REDIRECCIÓN La redirección es uno de los puntos fuertes del shell scripting, se utiliza generalmente para filtrar la salida de determinados programas, o para utilizar la salida de ciertos programas como entrada para otros. Pero veamos primero los conceptos de salida, entrada y error estandar. Cuando creamos un proceso (por ej, al ejecutar un comando), este tiene tres ficheros abiertos:
Lo que ocurre es que estos ficheros se pueden redireccionar. Es decir, podemos hacer que la salida estandar vaya a un fichero, en lugar de hacerlo en la pantalla, que la entrada estandar lea de un fichero, en lugar de hacerlo de teclado, y que el error estandar vaya a otro fichero. La redirección más básica es esta que acabo de poner como ejemplo, y es cuando se redirecciona a ficheros:
Además de este tipo de redirección existen otros, uno es el conocido comunmente como pipe. En él, se ejecutan a la vez dos programas, y la información que va generando uno, se utiliza como entrada para el otro. Por tanto, existe una especie de canal entre el fichero de salida de uno de ellos y el fichero de entrada del otro. Se hace mediante el símbolo '|' El ejemplo típico es: cat archivo | more El programa cat normalmente saca por pantalla (por su salida estandar) el contenido del fichero, pero en este caso lo que se hace es mandar esta información como entrada al programa more, que es un programa que muestra lo que le viene por la entrada estandar pantalla a pantalla. Estos son verdaderos tipos de redirección, pero aun se puede hacer otra cosa muy relacionada con estas redirecciones, aunque realmente no lo sea. Es la conversión de salida estandar a línea de comando. Se hace colocando un comando entre comas como esta: ` Que en teclados españoles está situada al lado de la ñ. Para generar esa coma, generalmente hay que pulsar un espacio despues de pulsarla. En algunos editores, para que salga hay que realizar esta operacion dos veces (tilde, espacio, tilde, espacio) Por ejemplo: for variable in `ls` do echo "FICHERO $variable ENCONTRADO" done Este ejemplo convierte la salida de ls (listado de los ficheros del directorio) a palabras de la linea de comandos, es decir, que si ls mostraba esto: c caml gtk images index.html perl shell sqlentonces, `ls` se ccambiará por esto: c caml gtk images index.html perl shell sqlen una sola línea y al lado del for variable in, por lo que se usara cada fichero como una palabra del comando FOR. En bash existe otra forma de hacer esto, que es usando $(comando), en lugar de `comando`, Pero es algo específico de bash, por lo que no recomiendo a nadie que lo use. Este es el momento de ver cómo evitar que los comandos saquen por pantalla información que no deseamos ver, como lo que ocurría con el ls cuando no se encontraba el fichero que le mandábamos buscar. En este caso nos interesabe sólo el valor que devolvía al sistema, pero ese mensaje de error, podemos eliminarlo redireccionando el error al dispositivo nulo (/dev/null). Este dispositivo elimina todo lo que se escribe en él. Con lo visto hasta aquí ya se pueden empezar a hacer algunas cosas, pero todavía faltan dos cosas importantes, en primer lugar el comando builtin test, que nos permite evaluar expresiones y devolver al sistema true o false para poder utilizarlo en if, while, etc. Y en segundo lugar, los comandos para filtrar ficheros (sed, grep, awk, etc.) test No voy a comentar todas las opciones del comando test, porque tecleando help test salen todas ellas muy bien explicadas, pero voy a explicar la forma abreviada y algunos detalles importantes. En primer lugar, test es un comando que evalua expresiones. Y tiene operadores para manejar strings, números y ficheros. Repito que en la ayuda del shell viene muy bien explicado. Además, tiene una forma abreviada, que consiste en rodear la expresión con corchetes en lugar de poner test expresión. Por ejemplo: if test -d /tmp then echo /tmp es un directorio else echo /tmp no es un directorio fies lo mismo que poner: if [ -d /tmp ] then echo /tmp es un directorio else echo /tmp no es un directorio fi Comandos para filtrar ficheros grep El comando grep (es un ejecutable del sistema) busca un patrón dentro de uno o más ficheros. Lo interesante es combinarlo con la redirección, y en concreto con los pipes. De esta forma, podemos por ejemplo, encontrar fácilmente el pid de un proceso que corre en el sistema: ps | grep bash Con este comando, aparecerán en pantalla todas las líneas de la salida del comando ps que contengan la palabra bash. Por tanto, saldrán los pids de todos los bash. sed El comando anterior no es suficiente, sólo es capaz de mostrar las líneas completas del fichero que contienen la palabra en cuestión. Esta carencia puede suplirse usando sed en uno de sus modos más útiles, el de substitución. Antes de contuniar, tengo que advertir que sed es una herramienta extremadamente compleja, prácticamente un lenguaje de scripting, pero generalmente, cuando las necesidades de nuestro script nos obligan a utilizar las funciones avanzadas de sed, bajo mi punto de vista lo mejor es hacer el script con perl directamente. Sed sería capaz de emular perfectamente el comportamiento de grep, veamos un ejemplo, el ejemplo anterior con grep, se puede hacer con sed asi: ps |sed -n /bash/p Por ahora no intenteis comprender esa línea :) Voy a intentar entonces dar unas nociones básicas de sed, hasta donde alcanzan mis conocimientos (yo todavía no controlo todo lo que sed permite hacer). Todo comando sed tiene dos partes:
SCRIPTS EN FICHEROS Los scripts, en cuando son útiles para más de una vez, en vez de memorizarlos se suelen almacenar en ficheros. Cuando un script está en un fichero, este debe comenzar con la línea: #!/bin/sh Que indica al sistema que es un script y que el programa capaz de analizarlo es /bin/sh. A continuación hay que dar permisos de ejecución al fichero: chmod +x ficheroy para ejecutarlo procederemos como con cualquier otro programa ejecutable. Comentarios Los comentarios se ponen con el símbolo #. Todo lo que tenga una línea a continuación de un # se ignora. PASO DE PARÁMETROS A SCRIPTS Cuando un script está en un fichero, tiene una enorme ventaja, y es que puede recibir parámetros, como los programas compilados. Los parámetros se pasan en variables numéricas (0,1,2,3,...). En el 0 como siempre está el nombre del ejecutable, y a partir de ahi, están los parámetros por el orden en que se escribieron. Lógicamente, como son variables, se accede a ellas con un símbolo $ delante ($0, $1, $2, ...). El número de parámetros se pasa en la variable # (si, como el comentario), por lo que se accede a ella con $#. Por último, para referirnos a todos los parámetros del script se utiliza la variable *, que referenciaremos como $*. FINALIZACIÓN Para finalizar un script se utiliza el comando exit. Debemos indicar un número que será el valor que devuelva al sistema. FUNCIONES Si señor, en Shell script se pueden hacer funciones. Y además de forma muy sencilla:nombre_funcion()
{
}
En nombre_función pondremos lo que nos apetezca. En shell cada función es
cómo un nuevo script, es decir, que tiene sus propios parámetros. Desde una
función no podemos acceder a ningún parámetro del script, pero desde el
script podemos llamar a la función con cualquier número de parámetros. De hecho,
si queremos que la función pueda usar los parámetros del script, basta con
llamarla con el parámetro $*, que está explicado antes.
Pues bien, esto es todo por ahora en cuanto a shell scripting, y no es poco, ya teneis material para hacer unos scripts verdaderamente grandes. Existen más comandos, en el caso de ejecutables esta claro, pero ademas existen más comandos built-in del shell. Pero ahora supongo que ya sabéis lo necesario para que os pique la curiosidad y probarlos vosotros mismos. Se ven con el comando help, como dije antes. EJEMPLO A modo de ejemplo voy a poner uno de mis scripts más sencillos y voy a comentarlo: #!/bin/sh if test $# -lt 1;then echo "$0 <words>";exit 1;fi for i in `find .`;do busqueda=`sed -n /$1/= $i` if test -n "$busqueda"; then echo $i: $busqueda;fi done 2>/dev/null Este script busca recursivamente en todos los directorios a partir del actual los ficheros que contengan la expresión regular que se le indica al script, y saca por pantalla el nombre del fichero y TODAS las líneas donde aparece dicha palabra. Paso a describir como funciona: En primer lugar se comprueba que el número de parámetros no es menor que 1, es decir, que el programa recibe al menos un parámetro. Si no es así, se pone la sintaxis en pantalla y se termina. En caso de superar la primera prueba, se entra en un bucle for, que obtiene las palabras del resultado de un find. Esto es peligroso, porque si hay algún fichero que tenga espacios, for lo interpretará como dos ficheros. No conozco ninguna forma trivial de solucionarlo, se pueden cambiar los espacios por caracterers raros y luego recuperarlos, pero esto es un poco chapucero. Dentro del for, primero asignamos a la variable búsqueda los números de línea que contienen la expresión regular, esto se hace muy fácilmente con el comando sed, que va procesando el fichero ($i) y sacando los números de línea donde aparece la palabra que se pasó como primer parámetro ($1). Luego se comprueba que la variable busqueda no es un string vacío, y si se verifica (es decir, apareció alguna línea en el fichero que contenia la expresion regular), se muestra en pantalla con el formato que se ve ahi. Al finalizar el bucle for, redireccionamos todo el error estandar al dispositivo /dev/null, que es precismente para eso, para poder mandar cosas a él, porque lo que se escribe en él se pierde.
Pues hasta aquí llegó esta introducción al scripting, espero que haya servido a alguien para aprender un poco sobre todo esto, aunque me temo que hayan quedado algunos puntos mal explicados. Por tanto, si veis que alguna cosa no se entiende, mandadme un email (<ryu@mundivia.es>) y lo cambiaré. |
| Los gráficos de esta página han sido creados con GIMP. |