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 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