PROGRAMACION SHELL-SCRIPT
=========================
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.
Con esta lección 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. Bueno mejor no lo haga. Presumir
no es bueno y ellos podrían presumir de su habilidad manejando el ratón.
El lenguaje shell-script es muy versatil pero ineficiente. Es mucho
más eficiente los programas escritos en lenguaje C que es el lenguaje
en el que se escribió el kernel de Linux y de otros muchos SO. El valor
del lenguaje shell-script es que permite hacer cosas complicadas con
poco esfuerzo. Algunas partes del SO están escritas en shell-script.
Por ejemplo muchos comandos que se van ejecutando en secuencia mientras
el sistema arranca son programas shell-script.
Después de hacer login en el sistema obtenedremos una sesión con el
interprete de comandos asociado a nuestro usuario. (Esta información
figura en /etc/passwd junto con la clave y otras cosas). 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 tuvieramos que teclear varias cosas podriamos
guardar estas ordenes en un fichero y redirigir la entrada estandar 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:
çCOM
PATH
/usr/local/bin
/home/usuario/bin
.bash_profile
Paso de parámetros:
-------------------
Imaginemos que usted tiene dificultad para pasar de ptas a euros,
y quiere hacer un programita para pasar de una moneda a otra
comodamente.
Teclee el comando siguiente:
çCOM
#pta2eu
echo $1 '/ 166.386' | bc -l
çFIN
pretende una descripcion exaustiva de la bourne-shell. Un buen metodo para
aprender a programar shell-script es intentar comprender las shell-script
que encontremos en el sistema con ayuda del manual online de unix, y
ejecutando algunas partes desde la shell para ver lo que hacen.
El lenguaje shell-script es un poco especial con relacion a otros lenguajes
ya que los blancos tienen un significado que hay que respetar.
set
set sin parametros lista las variables.
set -x Activa el modo traza. Similar al comando 'xtrace'
set +x Desactiva el modo traza.
set -v Similar a set -x. Y similar al comand verbose.
set +v Similar a set +x.
set -k Considera la asignacion aunque este precedida de un comando
set +k Lo contrario de lo anterior.
Es equivalente al comando 'keyword'.
set -a Hace exportables todas las variables.
Es equivalente al comando 'allexport'.
set -e Termina si un comando retorna un codigo distinto de 0.
Es equivalente al comando 'errexit'
set -u Trata variables no definidas como un error.
set -- No cambiar ninguna opcion. Util cuando $1 empieza por - o
por +.
unset Destruye una variable. Tiene el mismo efecto que
=
typeset Sin parametros muestra la lista de variables exportables.
typeset -i Considera variable numerica (integer).
funcion () Se pueden definir funciones que podran ser llamadas por
su nombre permitiendo el paso de parametros de la
misma forma que cuando se llama a una shell. Tambien
puede devolver codigos de retorno. Las variables alte-
radas dentro de una funcion tienen efecto visible fuera
de la funcion ya que una funcion no se ejecuta invocando
a una sub-shell. Una funcion se puede definir en un
shell-scrip o en la propia linea de comandos. Si se
hace esto ultimo sera como si aniadieramos un comando
interno. Existe un comando que permite acceder a para-
metros superiros al $9. Es el comand shift que actua
desplazando los parametros.
Ejemplo:
# Funcion que muestra dos parametros de entrada modifica una variable y
# devuelve un codigo de retorno.
funcion()
{
echo '<'$1'>'
echo '('$2')'
var=1234
return 33
}
## main ##
funcion 1 2
echo $?
echo $var
# Visualizar los parametros del 6 al 14
function()
{
shift 5
echo $1 $2 $3 $4 $5 $6 $7 $8 $9
}
## main ##
funcion 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
(lista) Ejecuta una lista de ordenes en una subshell.
Ejemplo:
$PS1> ( var1=11; var2=22; echo $var1 )
$PS1> echo $var1
$PS1> echo $var2
test
Test es un comando externo que devolvera un codigo de retorno 0 o
1 dependiendo del resultado de la expresion que sigua a continuacion.
Se usa muchisimo y tiene dos formatos. 'test ' o '[ ]'.
0 significara TRUE y distinto de 0 FALSE.
-r TRUE si el fichero existe y es legible.
-w TRUE si el fichero existe y se puede escribir.
-x TRUE si el fichero existe y es ejecutable.
-f TRUE si el fichero existe y es detipo regular (normal).
-d TRUE si existe y es un directorio.
-c TRUE si existe y es un dispositivo especial de
caracteres.
-b TRUE si existe y es un dispositivo especial de
bloques.
-p TRUE si existe y es un pipe (fifo).
-u TRUE si existe y tiene el bit set-user-ID.
-s TRUE si existe y tiene tamanio mayor que 0.
-z TRUE si es una cadena vacia.
-n TRUE si es una cadena no vacia.
= TRUE si ambas cadenas son identicas. OJO hay
dejar espacios a un lado y otro del signo igual para no
confundir la orden con una asignacion.
!= TRUE si son distintas.
TRUE si no es cadena nula. (Una variable que no existe devuelve
cadena nula.
-eq TRUE si ambos numero son iguales. (Otros operadores
algebraicos son -ne -gt -ge -lt -le)
! Operador logico NOT
-a Operador logico AND
-o Operador logico OR
( ) Parentesis para agrupar expresiones.
Comandos true y false
Son comandos que retornan el valor logico true o false respectivamente.
Operadores && y ||
Son operadores AND y OR a nivel de shell y poseen circuito de evalua-
cion corto.
Ejemplos:
# Como un OR queda evaluado cuando se encuentra el primer resultado TRUE
# o se completa toda la serie de operaciones, solo se evaluara el primer
# o comando, que de volvero TRUE.
$PS1> echo "hola" || echo "adios"
# El resultado sera 0
$PS1> echo $?
# Como un AND no queda hasta que se evalua el primer resultado FALSE
# o se completa la serie de operaciones, se evaluaran los dos comandos
# y devolvera igualmente TRUE.
$PS1> echo "hola" && echo "adios"
# El resultado sera 0
$PS1> echo $?
# Solo se visualizara 1 2 y 3
$PS1> echo 1 && echo 2 && echo 3 && false && echo 4 && echo 5
# Devolvera false
$PS1> echo $?
Sentencias condicionales
case Sirve para bifurcar entre varias posibilidades en funcion de la
evaluacion de un valor.
if Sentencia condicional simple.
Ejemplos:
# Ejecutar la funcion PASO01 o la funcion PASO02 dependiendo del valor
# de variable paso (1, 2 respectivamente). En caso de valor distinto de
# 1 y de 2 mandar un mensaje de 'Paso inexistente'.
case $paso in
1) PASO01 ;;
2) PASO02 ;;
*) echo "Paso inexistente" ;;
esac
# Si exite un fichero para lectura en la variable CMPRT01 imprimirlo.
if test -r $CMPRT01
then
lp $CMPRT01
fi
# Si La variable 'modo' contiene 'lp' imprimir el fichero en caso
# contrario sacarlo por pantalla.
if [ "$modo" = "lp" ]
then lp $FICH
else cat $FICH
fi
Bucles:
for Sirve para hacer un bucle con un numero conocido de iteraciones
while Sirve para hacer un bucle con un numero desconocido de itera-
ciones
break Provoca la salida de un bucle por el final. Si viene seguido
de un numero 'n' saldra de 'n' niveles.
continue Provoca un salto al comienzo del bucle para continuar con la
siguiente iteracion. Si viene seguida de un numero 'n' saldra
de 'n' niveles.
Ejemplos:
# 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 relacion de elementos contenido en lista ${tab[*]}.
# Como parte 5 la lista de parametros $1 $2 $3 .. etc,
# Como parte 6 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 ${tab[*]}
do
for m in $*
do
for n in `grep -l "CODIGO" *.doc`
do
echo $i.$j.$k.$l.$m.$n
done
done
done
done
done
done
# Desarrollar una funcion 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 aniadir esa
# palabra a la tabla.
gesttab(){
# Si numero de parametros distindo de 1 salir con error.
if [ $# -ne 1 ]
then
echo "Numero de parametros invalido en gesttab()"
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
}
# Hacer un bucle de captura por consola (terminara cuando se pulse
# enter ) y mostrar el resultado de cada captura entre parentesis.
LINEA="x" # Inicializar a un valor cualquiera
while test $LINEA
do
read LINEA
echo '('$LINEA')'
done
# El siguiente ejemplo funciona pero es una mala idea ya que supone
# Varias ejecuciones de un comando cuando se podia haber hecho con
# uno solo. ( Habria sido mejor hacer chmod +x * )
for i in $*
do
chmod +x $i
done
# Deseamos conocer las diferencias de el fichero patron con los
# fich1, fich2, y fich3.
for i in fich1 fich2 fich3; do diff patron $i; done
# Analizar el siguiente programa:
# Comprobar los comandos y las opciones desconocidas en el
# man. Ejecutar las partes dudosas desde la shell para poder
# comprenderlas. Explicar finalmente para que sirve y
# como funciona.
for i in `echo $PATH | tr ":" " "`
do
for j in `ls $i 2> /dev/null | grep -i $1 2> /dev/null`
do
echo `ls -liad $i/$j 2> /dev/null`
done
done
PROGRAMACION SHELL-SCRIPT
=========================
En este apartado se van a comentar mas cosas sobre la shell pero no se
pretende una descripcion exaustiva de la bourne-shell. Un buen metodo para
aprender a programar shell-script es intentar comprender las shell-script
que encontremos en el sistema con ayuda del manual online de unix, y
ejecutando algunas partes desde la shell para ver lo que hacen.
El lenguaje shell-script es un poco especial con relacion a otros lenguajes
ya que los blancos tienen un significado que hay que respetar.
set
set sin parametros lista las variables.
set -x Activa el modo traza. Similar al comando 'xtrace'
set +x Desactiva el modo traza.
set -v Similar a set -x. Y similar al comand verbose.
set +v Similar a set +x.
set -k Considera la asignacion aunque este precedida de un comando
set +k Lo contrario de lo anterior.
Es equivalente al comando 'keyword'.
set -a Hace exportables todas las variables.
Es equivalente al comando 'allexport'.
set -e Termina si un comando retorna un codigo distinto de 0.
Es equivalente al comando 'errexit'
set -u Trata variables no definidas como un error.
set -- No cambiar ninguna opcion. Util cuando $1 empieza por - o
por +.
unset Destruye una variable. Tiene el mismo efecto que
=
typeset Sin parametros muestra la lista de variables exportables.
typeset -i Considera variable numerica (integer).
funcion () Se pueden definir funciones que podran ser llamadas por
su nombre permitiendo el paso de parametros de la
misma forma que cuando se llama a una shell. Tambien
puede devolver codigos de retorno. Las variables alte-
radas dentro de una funcion tienen efecto visible fuera
de la funcion ya que una funcion no se ejecuta invocando
a una sub-shell. Una funcion se puede definir en un
shell-scrip o en la propia linea de comandos. Si se
hace esto ultimo sera como si aniadieramos un comando
interno. Existe un comando que permite acceder a para-
metros superiros al $9. Es el comand shift que actua
desplazando los parametros.
Ejemplo:
# Funcion que muestra dos parametros de entrada modifica una variable y
# devuelve un codigo de retorno.
funcion()
{
echo '<'$1'>'
echo '('$2')'
var=1234
return 33
}
## main ##
funcion 1 2
echo $?
echo $var
# Visualizar los parametros del 6 al 14
function()
{
shift 5
echo $1 $2 $3 $4 $5 $6 $7 $8 $9
}
## main ##
funcion 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
(lista) Ejecuta una lista de ordenes en una subshell.
Ejemplo:
$PS1> ( var1=11; var2=22; echo $var1 )
$PS1> echo $var1
$PS1> echo $var2
test
Test es un comando externo que devolvera un codigo de retorno 0 o
1 dependiendo del resultado de la expresion que sigua a continuacion.
Se usa muchisimo y tiene dos formatos. 'test ' o '[ ]'.
0 significara TRUE y distinto de 0 FALSE.
-r TRUE si el fichero existe y es legible.
-w TRUE si el fichero existe y se puede escribir.
-x TRUE si el fichero existe y es ejecutable.
-f TRUE si el fichero existe y es detipo regular (normal).
-d TRUE si existe y es un directorio.
-c TRUE si existe y es un dispositivo especial de
caracteres.
-b TRUE si existe y es un dispositivo especial de
bloques.
-p TRUE si existe y es un pipe (fifo).
-u TRUE si existe y tiene el bit set-user-ID.
-s TRUE si existe y tiene tamanio mayor que 0.
-z TRUE si es una cadena vacia.
-n TRUE si es una cadena no vacia.
= TRUE si ambas cadenas son identicas. OJO hay
dejar espacios a un lado y otro del signo igual para no
confundir la orden con una asignacion.
!= TRUE si son distintas.
TRUE si no es cadena nula. (Una variable que no existe devuelve
cadena nula.
-eq TRUE si ambos numero son iguales. (Otros operadores
algebraicos son -ne -gt -ge -lt -le)
! Operador logico NOT
-a Operador logico AND
-o Operador logico OR
( ) Parentesis para agrupar expresiones.
Comandos true y false
Son comandos que retornan el valor logico true o false respectivamente.
Operadores && y ||
Son operadores AND y OR a nivel de shell y poseen circuito de evalua-
cion corto.
Ejemplos:
# Como un OR queda evaluado cuando se encuentra el primer resultado TRUE
# o se completa toda la serie de operaciones, solo se evaluara el primer
# o comando, que de volvero TRUE.
$PS1> echo "hola" || echo "adios"
# El resultado sera 0
$PS1> echo $?
# Como un AND no queda hasta que se evalua el primer resultado FALSE
# o se completa la serie de operaciones, se evaluaran los dos comandos
# y devolvera igualmente TRUE.
$PS1> echo "hola" && echo "adios"
# El resultado sera 0
$PS1> echo $?
# Solo se visualizara 1 2 y 3
$PS1> echo 1 && echo 2 && echo 3 && false && echo 4 && echo 5
# Devolvera false
$PS1> echo $?
Sentencias condicionales
case Sirve para bifurcar entre varias posibilidades en funcion de la
evaluacion de un valor.
if Sentencia condicional simple.
Ejemplos:
# Ejecutar la funcion PASO01 o la funcion PASO02 dependiendo del valor
# de variable paso (1, 2 respectivamente). En caso de valor distinto de
# 1 y de 2 mandar un mensaje de 'Paso inexistente'.
case $paso in
1) PASO01 ;;
2) PASO02 ;;
*) echo "Paso inexistente" ;;
esac
# Si exite un fichero para lectura en la variable CMPRT01 imprimirlo.
if test -r $CMPRT01
then
lp $CMPRT01
fi
# Si La variable 'modo' contiene 'lp' imprimir el fichero en caso
# contrario sacarlo por pantalla.
if [ "$modo" = "lp" ]
then lp $FICH
else cat $FICH
fi
Bucles:
for Sirve para hacer un bucle con un numero conocido de iteraciones
while Sirve para hacer un bucle con un numero desconocido de itera-
ciones
break Provoca la salida de un bucle por el final. Si viene seguido
de un numero 'n' saldra de 'n' niveles.
continue Provoca un salto al comienzo del bucle para continuar con la
siguiente iteracion. Si viene seguida de un numero 'n' saldra
de 'n' niveles.
Ejemplos:
# 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 relacion de elementos contenido en lista ${tab[*]}.
# Como parte 5 la lista de parametros $1 $2 $3 .. etc,
# Como parte 6 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 ${tab[*]}
do
for m in $*
do
for n in `grep -l "CODIGO" *.doc`
do
echo $i.$j.$k.$l.$m.$n
done
done
done
done
done
done
# Desarrollar una funcion 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 aniadir esa
# palabra a la tabla.
gesttab(){
# Si numero de parametros distindo de 1 salir con error.
if [ $# -ne 1 ]
then
echo "Numero de parametros invalido en gesttab()"
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
}
# Hacer un bucle de captura por consola (terminara cuando se pulse
# enter ) y mostrar el resultado de cada captura entre parentesis.
LINEA="x" # Inicializar a un valor cualquiera
while test $LINEA
do
read LINEA
echo '('$LINEA')'
done
# El siguiente ejemplo funciona pero es una mala idea ya que supone
# Varias ejecuciones de un comando cuando se podia haber hecho con
# uno solo. ( Habria sido mejor hacer chmod +x * )
for i in $*
do
chmod +x $i
done
# Deseamos conocer las diferencias de el fichero patron con los
# fich1, fich2, y fich3.
for i in fich1 fich2 fich3; do diff patron $i; done
# Analizar el siguiente programa:
# Comprobar los comandos y las opciones desconocidas en el
# man. Ejecutar las partes dudosas desde la shell para poder
# comprenderlas. Explicar finalmente para que sirve y
# como funciona.
for i in `echo $PATH | tr ":" " "`
do
for j in `ls $i 2> /dev/null | grep -i $1 2> /dev/null`
do
echo `ls -liad $i/$j 2> /dev/null`
done
done
Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos |
|