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.
|
Que es un shell-script
Con esta lección y las siguientes nos acercamos al momento en que usted
podrá presumir
ante algunos de sus amigos de haber realizado en su flamante SO cosas
que ellos no saben hacer con Güindos, aunque ellos podrían presumir de
su habilidad cerrando las ventanas del General Failiure y del Doctor
Guasón.
El lenguaje shell-script es muy versátil aunque hay que advertir que
es bastante ineficiente. Son mucho
más eficientes los programas escritos en lenguaje C. Este es el lenguaje
en el que se escribió el kernel de Linux y otros muchos SO. El Bash no
es tan eficiente como el C. El valor
del lenguaje shell-script es que permite hacer cosas complicadas con
muy poco esfuerzo en perfecta combinación con una serie de comandos
también muy potentes disponibles en Linux . Verdad que suena interesante ?
Algunas partes del SO que no pertenecen al kernel están escritas en
shell-script.
Por ejemplo muchos comandos que se van ejecutando en secuencia mientras
el sistema arranca son programas realizados en shell-script así que
la ineficiencia no es ningún obstáculo para ciertas tareas. Por el contrario
para un administrador tener ciertos programas del sistema en shell-script
le permite retocarlos a su gusto con gran facilidad.
Después de hacer login en el sistema obtendremos una sesión con el
interprete de comandos asociado a nuestro usuario. (Esta asociación
figura en el fichero '/etc/passwd' junto quizás con la clave encriptada
y otras cosas). Decimos quizás porque la clave no siempre es guardada
en este fichero. La razón es que pese a estar encriptada /etc/passwd es
un fichero que cualquiera puede leer y resulta más seguro otro sistema
llamado shadow. Que /etc/passwd sea legible nos permite consultar
entre otras cosas cual es nuestra shell en el inicio de una sesión.
Desde esta sesión podemos ir introduciendo ordenes desde el teclado pero nuestro
interprete de comandos no deja de ser a su vez un programa normal como
cualquier otro, así que si tuviéramos que teclear varias cosas podríamos
guardar estas ordenes en un fichero y redirigir la entrada estándar de
la shell para que tome la entrada desde el fichero en lugar de hacerlo
desde el teclado.
Vamos a ver un ejemplo para mostrar fecha y hora en letras grandes.
Teclee el comando siguiente:
Ahora vamos a practicar repasando un poco las nociones que ya
vimos en el primer capítulo dedicado al shell script porque
son conceptos que conviene asentar. Para ello vamos a guardar
este comando en un fichero para poder ejecutarlo
cuando nos apetezca. Previamente nos situamos en el directorio
'/tmp' como es costumbre y nos aseguramos de no estar usando el usuario
root ni ningún otro usuario con privilegios especiales.
!! Vaya !! Esto no es lo que queríamos hacer. Este comando siempre mostraría
la misma fecha y hora y seguramente no pretendíamos eso. Dado que hemos
usado las dobles comillas se ha producido la expansión de la línea de
ordenes concretamente el operador grave. Si en lugar de las dobles comillas
usáramos las comillas simples no se habría producido ese error pero existen
ya comillas simples dentro del propio comando y la shell tomaría el cierre
de la comilla simple en un lugar que no es el que nosotros deseábamos. Todo
esto lo comentamos a modo de repaso. Entonces como tendríamos que haber
generado este comando ? Aquí es donde el listillo dice que usando un
editor de texto. Bueno al fin de cuentas ya sabemos usar el 'vi' no ?
Bueno pues vamos primero a hacerlo sin editor.
Recuerda que mencionamos la posibilidad de escapar un
solo carácter con el carácter '\' ? Intentemos de nuevo.
Ahora si. Vamos a usar la shell de la misma forma que haríamos con un
programa normal redirigiendo la entrada estándar desde este fichero.
Por lo tanto el fichero tiene tratamiento de datos y solo se requiere
permiso de lectura para ejecutarlo de esta forma.
Para ejecutarlo sin redirigir entrada salida podemos pasar como
argumento el nombre del fichero. Nuevamente solo hará falta permiso
de lectura.
Ejecución de un shell-script directamente como un comando
Lo del './' antes del ejecutable resultará necesario si el directorio
donde se encuentra el ejecutable no figura en la variable '$PATH'
En realidad ocurre algo muy similar al caso anterior porque también
se arranca una sub-shell que toma como entrada el fichero.
Esto ocurre cuando la shell detecta que el ejecutable está en formato
de texto. Si se tratara de código binario lo ejecutaría sin arrancar
una sub-shell. Para averiguar que tipo de fichero se trata, la shell
mirará los
primeros cráteres que contiene. Los ficheros ejecutables suelen tener
un par de bytes iniciales que servirán para identificar el tipo de fichero.
En caso contrario se asume que es un shell-srcip.
A estos dos caracteres iniciales juntos forman lo que se denomina número
mágico. Es solo una cuestión de nomenclatura. El SO lo ve como un número
entero de dos bytes y nosotros desde un editor lo vemos como dos caracteres.
Existe un programa llamado 'file' que utiliza esta información así
como la presencia de ciertos patrones para identificar el tipo de un
fichero.
Se usa el término script para ficheros que son legibles y ejecutables.
Es decir el propio fuente del programa es ejecutable. Fuente de un programa
es lo que escribe el programador. El lenguaje C no es un lenguaje tipo
script ya que el fuente no es ejecutable. Hace falta un compilador que
traduce el código fuente a código propio de la máquina. Los Script no se
ejecutan directamente en la máquina sino dentro de un programa que va
interpretando las instrucciones. Bueno en general esto es más o menos así.
Hay muchos tipos de scripts. La shell tiene su propio lenguaje. Existe
un lenguaje distinto llamado 'perl' que también se ejecuta como un script.
'perl' es un lenguaje inspirado en el uso combinado del interprete de
comandos y algunos comandos clásicos de los SO tipo Unix, y además incorpora
cosas de otros lenguajes como el 'C' y otras cosas totalmente originales.
'perl' no es fácil de aprender pero es un lenguaje de script muy potente.
Un script de 'perl' se puede ejecutar mediante el comando 'perl prueba.pl'.
El fichero 'prueba.pl' deberá estar en lenguaje 'perl'. Si ejecutáramos
directamente './prueba.pl' desde bash, el interprete de comandos intentará
ejecutarlo como si fuera un script de bash dando errores de sintaxis.
Para evitar esto se puede poner totalmente al principio del fichero los
caracteres '#!' Estos caracteres serán vistos como un número mágico que
avisa que a continuación viene el programa a usar con ese script.
Para el script de perl debería haber empezado con '#!/usr/bin/perl' para
poderlo ejecutar directamente sin problema.
Para un script de bash no es necesario poner nada pero podríamos
haber puesto '#!/bin/bash', o '#!/bin/sh'. De esta forma también se
puede indicar cual es la shell que deseamos usar ya que podríamos
desear usar otras shells como '#!/usr/bin/csh' para la C-shell o
'#!/usr/bin/ksh' para la Korn shell. A nosotros con el bash nos
basta y sobra.
Ejecución con la shell-actual
Existe una forma de decirle
a la shell actual que lea y ejecute una serie de ordenes por si misma
sin arrancar una sub-shell. Para ello hay que anteponer un punto y un
blanco al nombre del comando. Nuevamente solo hará falta permiso de lectura.
En este caso no se ha ejecutado mediante una sub-shell pero no
se aprecia ninguna diferencia. Esto no siempre es así.
Veamos un ejemplo en el que se aprecie la diferencia. $$ tomará el
valor del pid de la shell en ejecución y $PPID tomará el valor del
pid del proceso padre.
Evidentemente cuando pruebe este ejemplo obtendrá un pid distinto de
213 pero lo interesante es ver como ambas ejecuciones establecen claramente
que la primera fue efectuada por un proceso hijo de la shell
que se ejecuta en segundo lugar usando el punto, seguido del espacio y
del 'fichero_ordenes'.
Los procesos hijos heredan el entorno (variables y otras cosas) desde
el proceso padre por lo cual un padre si puede comunicarse con su hijo
de esa forma. Por el contrario un proceso hijo (sub-shell en nuestro
caso) jamás puede comunicarse de esa forma con su padre. Resulta imposible
que un proceso hijo comunique valores de variables del entorno a su padre.
Si deseamos modificar variables del entorno ejecutando un shell-script
no podremos hacerlo nunca si la ejecutamos desde dentro de una sub-shell.
Por el contrario deberemos usar algo del tipo '. ./fichero_ordenes' como
acabamos de ver.
Existe un fichero en el directorio inicial de cada usuario que
consiste en una serie de ordenes iniciales para shell. En el
caso de la Bash este fichero es '~/.bash_profile'. Podemos ejecutarlo
si queremos pero este tipo de ficheros incluyen ordenes para inicializar
variables entre otras cosas, así que lo adecuado sería ejecutarlo desde
la shell actual. Haga lo siguiente:
Observe que hay tres puntos en el último comando con tres significados
distintos. El primero es para ejecutar con la shell-actual, el segundo
es para indicar el directorio actual, y el último forma parte del nombre
y se usa para que no sea tan visible dado que un punto al principio del
nombre de un fichero no será expandido por la shell cuando usemos '*'.
Vamos a comprobar la acción del número mágico.
Pase a un directorio para practicar sus ejercicios, y
edite un nuevo fichero que debe llamarse 'aviso'.
Ahora edite otro nuevo fichero que llamaremos 'comando'.
Vamos a ejecutar nuestro shell-script de dos formas distintas y
observamos que el resultado no es el mismo. En el segundo caso
se ejecuta el script 'aviso'. Este último ejemplo no es completo
ya que '/bin/bash /tmp/aviso' no actuará interpretando sino de una
forma independiente del texto y mostrará siempre un mensaje de
aviso.
La explicación es la siguiente. El número mágico indica con que shell
se ejecutará el script. Nosotros hemos puesto en lugar una shell sin
argumetos que ejecutaría ese texto una shell que recibe un argumento.
Eso hace que se ejecute el comando pasado como argumento en lugar de
tomar el texto del fichero que contiene el número mágico. Cuando
ejecutamos con la shell actual la información siguiente al número mágico
no será tenida en cuenta porque supondría arrancar una sub-shell.
Es un ejemplo rebuscado pero hay que intentar comprender lo que sucede
en distintas situaciones porque el lenguaje shell-script tiene sus
rarezas.
Ejecutar en una subshell usando paréntesis
Estamos avanzando muy deprisa, y por eso tenemos que practicar bastante
para consolidar los conocimientos. No siempre resulta trivial averiguar
como funcionan las cosas. Observe el siguiente ejemplo:
Nuevamente el resultado puede ser distinto en su ordenador pero
lo importante es que 'echo $$' parece obtener el mismo pid pese
a ocurrir en distintas sub-shells. Cuando parece ocurrir algo
distinto de lo que nosotros esperábamos resulta muy útil usar el
modo traza con 'set -x'. Repetimos el ejemplo misterioso de antes.
Misterio aclarado. Realmente no ejecutamos ningún 'echo $$' sino que
ya se ejecuta directamente 'echo 218' porque la shell lo primero que
hizo antes de ejecutar las ordenes fué expandir cada '$$' presente
en la línea de ordenes.
Ahora que ya sabemos que es un shell-script vamos a ir aprendiendo cosas
pero no se pretende una descripción exhaustiva de la Bash. Puede consultar
con man bash las dudas que tenga. Recuerde que el man no es una herramienta
de estudio sino de consulta. Cuando consulte 'man bash' verá cantidad de
información relativa a cosas que no hemos explicado y man no explica casi
nada. Por ello muchas cosas de 'man bash' no las entederá aunque siempre
queda el recurso de probar las cosas que no se comprenden para ver como
funcionan.
Se irán proponiendo ejemplos que ilustren unas cuantos conceptos básicos.
Una vez que haya practicado con estos ejemplos habremos conseguido dar
los primeros pasos en la programación de shell-script que son los que
más cuestan. Después un buen método para continuar aprendiendo a programar
en shell-script es intentar comprender las shell-script
que encontremos en el sistema con ayuda del manual online de unix.
El lenguaje shell-script es un poco especial con relación a otros lenguajes
ya que los blancos y los saltos de línea tienen un significado que hay que
respetar y la expansión
de ordenes es algo que puede resultar desconcertante. Eso lo hace
un poco antipático al principio. Es importante tener una buena base de
conocimientos que en estos momentos ya tenemos, y continuar practicando.
Paso de parámetros a un shell-script
En este caso $1 actúa como una variable que tomará el valor del primer
parámetro. $0 toma el valor del propio comando ejecutado y $2, $3 etc
el valor de los parámetros segundo y tercero respectivamente. La variable
$# tomará el valor del número de parámetros que usemos.
La variable $* toma el valor de todos los parámetros concatenados.
Vamos a poner un ejemplo que llamaremos 'pru_parametros'. Debe editarlo,
darle permisos de ejecución con chmod +x por ejemplo
y ejecutarlo pasando distintos parámetros. './pru_parametros', './pru_parametros hola', './pru_parametros pararm1 param2 param3'.
La ejecución de esta prueba quedaría como sigue.
No existe la posibilidad de usar $10 para acceder al parámetro
décimo. En lugar de acceder de esta forma habría que usar un comando
interno llamado 'shift'. El efecto de este comando es desplazar los
argumentos una posición. De esta forma $1 se pierde y $2 pasará a
ser $1, $3 pasará a ser $2, etc. Si existían más de 10 parámetros
ahora el décimo que antes era inaccesible pasará a ser $9. $0 no varía.
Se pueden desplazar varios parametros de un golpe pasando un número
a la función 'shift' por ejemplo 'shift 5' desplazaría a 5 parámetros.
Vamos a editar un fichero de ejemplo que llamaremos 'pru_shift':
Debe editarlo, darle permisos de ejecución con 'chmod +x pru_shift'
y ejecutarlo pasando distintos parametros. './pru_shift pararm1 param2 param3'.Observe que es una prueba parecida a la anterior. Intente utilizar sus
conocimientos de vi para no tener que teclear tanto. Recuerde que puede
leer un fichero desde dentro del editor y que puede duplicar bloques
de información de varias formas.
La ejecución de esta prueba quedaría como sigue.
Test
Ya vimos hace algunas lecciones una introducción al interprete de
comandos de Linux (shell). Vamos a ampliar nuestros conocimientos
sobre la shell y si bien antes consideramos a la shell como un
interprete de comandos en esta lección veremos que es mucho más que
eso. La shell de Linux que vamos a usar es la Bash que como ya dijimos
es un superconjunto de la Bourne-Shell. Sin lugar a dudas esta lección
le permitirá dar un salto enorme en el aprovechamiento de su SO Linux.
$ banner `date '+%D %T'`
$ # Nos situamos en /tmp
$ cd /tmp
$ # Generamos el fichero con el comando
$ echo "banner `date '+%D %T'`" > fichero_ordenes
$ # Comprobamos el contenido del fichero
$ cat fichero_ordenes
banner 08/02/00 20:26:30
$ # Generamos el fichero con el comando
$ echo "banner \`date '+%D %T'\`" > fichero_ordenes
$ # Comprobamos el contenido del fichero
$ cat fichero_ordenes
banner `date '+%D %T'`
$ # Ejecutamos
$ bash < fichero_ordenes
$ # También podemos ejecutarlo de otra forma
$ cat fichero_ordenes | bash
$ # Ejecutar pasando el nombre del fichero como argumento
$ sh fichero_ordenes
$FIN
Para ejecutarlo directamente como un comando necesitamos darle permiso
de ejecución.
$ # Ejecutar pasando el nombre del fichero como argumento
$ chmod +x fichero_ordenes
$ ./fichero_ordenes
$FIN
Hasta este momento siempre hemos arrancado una sub-shell que leía
las ordenes del fichero, las ejecutaba y después terminaba y moría
cediendo el control nuevamente a la shell original que arrancó la sub-shell.
$ # Ejecutar ordenes de un fichero desde la shell actual
$ . ./fichero_ordenes
$echo echo \$PPID \$$ > fichero_ordenes
$ bash fichero_ordenes
213 2396
$ . ./fichero_ordenes
1 213
$ cd
$ . ./.bash_profile
echo "Error este shell-script debió arrancarse con . /comando"
#!/bin/bash /tmp/aviso
echo "COMANDO"
$ # (1) Se ejecuta usando una sub-shell pero en este
$ # caso se pasa un argumento a la sub-shell y por ello
$ # no se ejecuta el código del fichero comando.
$ chmod +x aviso
$ ./comando
Error este shell-script debió arrancarse con . /comando
$ # (2) Ahora ejecutamos con la shell actual
$ . ./comando
COMANDO
Ya que estamos con el tema de la subshell vamos a comentar como funciona
el uso de los paréntesis desde la linea de órdenes. No es necesario
realizar un script para ejecutar algo en una subshell
(lista) Ejecuta una lista de ordenes en una subshell.
$ var1=888
$ ( var1=111; echo $var1 )
111
$ echo $var1
888
$ echo $$ ; ( echo $$ ; ( echo $$ ) )
218
218
218
$ set -x
$ echo $$ ; ( echo $$ ; ( echo $$ ) )
+ echo 218
218
+ echo 218
218
+ echo 218
218
$ set +x
Imaginemos que usted tiene dificultad para pasar de Ptas a Euros,
y quiere hacer un programita para pasar de una moneda a otra
cómodamente.
Edite el comando siguiente, y luego ejecutelo en la forma 'pta2eu 35':
#pta2eu
echo $1 '/ 166.386' | bc -l
# pru_parametros
echo "Numero de parámetros = $#"
echo '$0=' $0
echo '$1=' $1
echo '$2=' $2
echo '$3=' $3
echo '$*=' $*
$ ./pru_parametros
Numero de parámetros = 0
$0= ./pru_parametros
$1=
$2=
$3=
$*=
$ ./pru_parametros hola
Numero de parámetros = 1
$0= ./pru_parametros
$1= hola
$2=
$3=
$*= hola
$ ./pru_parametros pararm1 param2 param3
Numero de parámetros = 3
$0= ./pru_parametros
$1= pararm1
$2= param2
$3= param3
$*= pararm1 param2 param3
$
# pru_shift
echo "Numero de parámetros = $#"
echo '$0=' $0
echo '$1=' $1
echo '$2=' $2
echo '$3=' $3
echo '$*=' $*
shift
echo "Numero de parámetros = $#"
echo '$0=' $0
echo '$1=' $1
echo '$2=' $2
echo '$3=' $3
echo '$*=' $*
$ ./pru_shift pararm1 param2 param3
Numero de parámetros = 3
$0= ./pru_shift
$1= pararm1
$2= param2
$3= param3
$*= pararm1 param2 param3
Numero de parámetros = 2
$0= ./pru_shift
$1= param2
$2= param3
$3=
$*= param2 param3
Puede comprobar sus conocimientos respondiendo el siguiente test.
Para ello seleccione las opciones que se ajusten a la verdad y luego
pulse el boton para ver el resultado de su test.
Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos