Programación del Shell LinuxAquí tenemos un libro libre y completo sobre ShellLa sed del "conocimiento libre" es muy bienvenida. |
Comandos Shell Script |
![]() |
|
Home |
Artículos ![]() ![]() |
Comprar el libroChangelogs |
|
Conversación de Bar - Parte X
-Que hay amigo, te lo puse mas fácil, verdad? Un ejercicio muy simple...
- Si, pero en los tests que hice y de acuerdo con lo que me enseñaste sobre substitución de parámetros, me pareció que debería hacer otras alteraciones en las funciones que creamos, para dejarlas de uso más general como me dijiste que todas las funciones deberían de ser, quieres ver?
- Claro, si te pedí hacerlas es porque estoy con ganas de verte aprender, pero alto! dame un momento!
- Mozo! Trae dos, uno sin espuma!
- Anda, enseñame lo que hiciste.
- Bien, además de lo que me pediste, me fije que el programa que llamaba la función, tendría que tener previamente definida la línea en que sería dado el mensaje y la cantidad de columnas. Lo que hice fue incluir dos líneas - en las cuales emplee sustitución de parámetros - y en caso de que una de estas variables no fuese introducida, la propia función la generaría. La línea del mensaje estaría tres líneas encima del final de la pantalla y el total de columnas sería obtenido por el comando tput cols. Mira como quedó: $ cat pergunta.func
# La función recibe 3 parámetros en el siguiente orden:
# $1 - Mensaje a ser dado en pantalla
# $2 - Valor a ser aceptado como respuesta default
# $3 - Otro valor aceptado
# Suponiendo que $1=Acepta?, $2=s y $3=n, la línea
# abajo colocaría en Msj el valor "Acepta? (S/n)"
TotCols=${TotCols:-$(tput cols)} # Si no estaba definido, ahora lo está
LineaMesj=${LineaMesj:-$(($(tput lines)-3))} # Idem
Msj="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)"
TamMsj=${#Msj}
Col=$(((TotCols - TamMsj) / 2)) # Para centrar Msj en la línea
tput cup $LineaMesj $Col
read -n1 -p "$Msj " SN
SN=${SN:-$2} # Si vacio coloca default en SN
SN=$(echo $SN | tr A-Z a-z) # La salida de SN será en minúscula
tput cup $LineaMesj $Col; tput el # Borra msj de pantalla
- Me gustó, te anticipaste a lo que te iba a pedir. Solamente para cerrar esta conversación de sustitución de parámetros, fíjate que la legibilidad es horrible, pero la optimización, o sea, la velocidad de ejecución, está óptima. Como las funciones son cosas muy personales, ya que cada uno usa las suyas, y casi no se les da mantenimiento, yo siempre opto por la optimización.
- Hoy vamos a salir de aquel aburrimiento que fue nuestra última conversación y volveremos a la lógica saliendo de la memorización, pero te vuelvo a recordar, todo lo que te mostré la otra vez aquí en el Bar, es válido y de gran ayuda, guarda aquellas servilletas que escribimos que tarde o temprano te van a ser muy útiles. El comando eval
- Te voy a dar un problema que dudo que resuelvas:
$ var1=3
$ var2=var1
- Te dí estas dos variables, y quiero que me digas como puedo, solamente refiriéndome a
- Ah! eso es fácil, es sólo hacer: echo $`echo $var2`
- Fíjate que coloqué el echo
- A sí? Entonces ejecutalo a ver si está correcto. $ echo $`echo $var2`
$var1
- Eh! Que pasó? Mi razonamiento parecía bastante lógico...
- Tu razonamiento realmente fue lógico, el problema es que te olvidaste de una de las primeras cosas de que te hablé aquí en el Bar y voy a repetir. El Shell usa el siguiente orden para resolver una línea de comandos:
De esta forma, cuando llegó a la fase de resolución de variables, que como ya dije es anterior a la ejecución, la única variable existente era
Problemas de este tipo son relativamente frecuentes y serían insolubles en caso de que no existiese la instrucción eval cmd
Donde
De esta forma si ejecutásemos el comando que propusiste, colocando el $ eval echo $`echo $var2`
3
Este ejemplo también podría haber sido hecho de la siguiente manera: $ eval echo \$$var2
3
En la primera pasada la barra invertida (
Ahora voy a colocar un comando dentro de
$ var2=ls
Voy a ejecutar: $ $var2
10porpag1.sh alo2.sh listamusica logaute.sh
10porpag2.sh confuso listartista mandamsj.func
10porpag3.sh contpal.sh listartista3 monbg.sh
alo1.sh incusu logado
Ahora vamos a colocar en $ var2='ls $var1'
$ var1='l*'
$ $var2
ls: $var1: No such file or directory
$ eval $var2
listamusica listartista listartista3 logado logaute.sh
Nuevamente, al momento de la sustitución de las variables,
Una vez un colega de una excelente lista sobre Shell Script, presentó una duda: quería hacer un menú que numerase y listase todos los archivos con extensión $ cat fazmenu
#!/bin/bash
#
# Lista numerando los programas con extensión .sh en
# directorio actual y ejecuta el escogido por el operador
#
clear; i=1
printf "%11s\t%s\n\n" Opción Programa
CASE='case $opt in'
for arq in *.sh
do
printf "\t%03d\t%s\n" $i $arq
CASE="$CASE
"$(printf "%03d)\t %s;;" $i $arq)
i=$((i+1))
done
CASE="$CASE
*) . error;;
esac"
read -n3 -p "Introduce la opción deseada: " opt
echo
eval "$CASE"
Parece complicado porque usé mucho el Vamos a ejecutarlo para ver la salida generada: $ fazmenu.sh
Opción Programa
001 10porpag1.sh 002 10porpag2.sh 003 10porpag3.sh 004 alo1.sh 005 alo2.sh 006 contpal.sh 007 fazmenu.sh 008 logaute.sh 009 monbg.sh 010 readpipe.sh 011 redirread.sh Introduce la opción deseada:
En este programa sería interesante tener una opción de escape, y para eso sería necesario la inclusión de una línea después del loop de montaje de la pantalla y alterar la línea en la cual hacemos la atribución final del valor de la variable $ cat fazmenu
#!/bin/bash
#
# Lista numerando los programas con extensión .sh en
# directorio actual y ejecuta el escogido por el operador
#
clear; i=1
printf "%11s\t%s\n\n" Opción Programa
CASE='case $opt in'
for arq in *.sh
do
printf "\t%03d\t%s\n" $i $arq
CASE="$CASE
"$(printf "%03d)\t %s;;" $i $arq)
i=$((i+1))
done
printf "\t%d\t%s\n\n" 999 "Fin del programa" # línea incluida
CASE="$CASE
999) exit;; # línea alterada
*) ./error;;
esac"
read -n3 -p "Introduce la opción deseada: " opt
echo
eval "$CASE"
Señales de ProcesosExiste en Linux una cosa llamada señal (signal). Existen diversas señales que pueden ser mandadas para (o generados por) procesos en ejecución. Vamos de aqui en adelante a dar una ojeada a las señales enviadas hacia los procesos y más adelante vamos a dar una pasada rápida por las señales generados por procesos. señales asesinas
Para mandar una señal a un proceso, usamos normalmente el comando kill -sig PID
Donde
Además de estas señales, existe el tan abusado
En fin, existen mil razones para no usar un El trap no atrapa
Para hacer el control de procesos descripto antes, existe el comando trap "cmd1; cmd2; cmdn" S1 S2 ... SN o trap 'cmd1; cmd2; cmdn' S1 S2 ... SN
Donde los comandos
Las comillas (") o los apóstrofes (') sólo son necesarias en el caso de que el
Para entender el uso de las comillas (") y los apóstrofes (') vamos a recurrir a un ejemplo que trata un fragmento de un script que hace un ftp -ivn $RemoComp << FimFTP >> /tmp/$$ 2>> /tmp/$$ user $Fulano $Secreto binary get $Arq FimFTP
Observa que, tanto las salidas de los diálogos del
En el caso de que este trap "rm -f /tmp/$$ ; exit" 0 1 2 3 15
Así en el caso de que hubiese una interrupción brusca (señales
En el caso de que en la línea de comandos del
Este trap "rm -f /tmp/$$" 0 trap "exit" 1 2 3 15
Así al recibir una de las señales el programa terminaría, y al terminar, generaría una señal
Observa también que el Shell analiza la línea de comandos, una vez cuando el
Si deseas que la sustitución sea realizada solamente en el momento de ser recibida la señal, el comando debería estar colocado entre apóstrofes (
trap 'rm -f /tmp/$$ ; exit' 0 1 2 3 15
Suponte dos casos: tu tienes dos scripts que llamaremos
Cuando se ejecuta el comando
Si la línea de comandos del trap "" 2
Especifica que la señal de interrupción ( trap 2
Si se ignora una señal, todos los Subshells ignoraran esta señal. Por lo tanto, si tu especificas que acción debe ser tomada cuando se reciba una señal, entonces todos los Subshells también tomaran la misma acción cuando reciban esta señal, o sea, las señales son automáticamente exportadas. Para la señal que hemos mostrado (señal Suponte que ejecutes el comando:
trap "" 2 y entonces ejecutes un Subshell, que volverá a ejecutar otro script como un Subshell. Si se generase una señal de interrupción, esta no tendrá efecto sobre el Shell principal ni sobre los Subshell por él llamados, ya que todos ellos ignorarán la señal. Otra forma de restaurar una señal a su patrón (default) es haciendo: trap - señal
En korn shell ( echo -n "Señal: " stty -echo read Señal stty echo
El problema en este tipo de construcción es que en el caso de que el operador no supiese la señal, probablemente haría un echo -n "Señal: " trap "stty echo exit" 2 3 stty -echo read Señal stty echo trap 2 3 Para terminar este asunto, abre un terminal gráfico y escribe en el prompt de comando lo siguiente: $ trap "echo Cambió el tamaño de la ventana " 28
En seguida, coge el mouse (arghh!!) y arrástralo para variar el tamaño de la ventana actual. Sorprendido? Es el Shell orientado a eventos... Uno mas, porque no me puedo resistir... Ahora escribe esto: $ trap "echo acabó" 17
En seguida haz: $ sleep 3 &
Acabas de crear un subshell que dormirá durante tres segundos en background. Al final de este tiempo, recibirás un mensaje Para volver estas señales a sus opciones por defecto, haz: $ trap 17 28
O: $ trap - 17 28
Acabamos de ver otras dos señales que no son tan importante como las que vimos anteriormente, pero voy a registrarlas en la tabla siguiente:
Muy bueno este comando, verdad? Si tu descubres algun caso interesante del uso de señales, por favor informame por e-mail porque es muy rara la literatura sobre el asunto. Comando getopts
El comando Sintáxis: getopts cadenadeopciones nombre
La -a argumento
Normalmente uno o más espacios en blanco separan el parámetro de la opción; al mismo tiempo, -aargumento
El
Como ya sabemos, cada opción pasada por una línea de comandos tiene un índice numérico, así, la primera opción estará contenida en
Cuando una opción tiene un argumento asociado (indicado por El comando termina su ejecución cuando:
El ejemplo siguiente es meramente didáctico, y sirve para mostrar, en un pequeño fragmento de código, el uso pleno del comando. $ cat getoptst.sh
#!/bin/sh
# Ejecute así: # # getoptst.sh -h -Pimpressora arch1 arch2 # # y note que las informaciones de todas las opciones son exhibidas # # La cadena 'P:h' dice que la opción -P es una opción compleja # y requiere de un argumento, y que h es una opción simple que no requiere # argumentos. while getopts 'P:h' OPT_LETRA do echo "getopts hizo la variable OPT_LETRA igual a '$OPT_LETRA'" echo " OPTARG es '$OPTARG'" done used_up=`expr $OPTIND - 1` echo "Ignorando los primeros \$OPTIND-1 = $used_up argumentos" shift $used_up echo "Lo que sobró de la línea de comandos fue '$*'" Para entenderlo mejor, vamos a ejecutarlo como está sugerido en su encabezado: $ getoptst.sh -h -Pimpresora arch1 arch2
getopts hizo la variable OPT_LETRA igual a 'h'
OPTARG es ''
getopts hizo la variable OPT_LETRA igual a 'P'
OPTARG es 'impresora'
Ignorando los primeros $OPTIND-1 = 2 argumentos
Lo que sobró de la línea de comandos fue 'arch1 arch2'
De esta forma, sin tener mucho trabajo, separé todas las opciones con sus respectivos argumentos, dejando solamente los parámetros que fueron pasados por el operador para un tratamiento posterior .
Fíjate que si hubiesemos escrito la línea de comando con el argumento ( $ getoptst.sh -h -P impresora arch1 arch2
getopts hizo la variable OPT_LETRA igual a 'h'
OPTARG es ''
getopts hizo la variable OPT_LETRA igual a 'P'
OPTARG es 'impresora'
Ignorando los primeros $OPTIND-1 = 3 argumentos
Lo que sobró de la línea de comandos fue 'arch1 arch2'
En el ejemplo siguiente, fíjate que si pasamos una opción inválida, la variable $ getoptst.sh -f -Pimpresora arch1 arch2 # La opción ?f no es válida
./getoptst.sh: illegal option -- f
getopts hizo la variable OPT_LETRA igual a '?'
OPTARG es ''
getopts hizo la variable OPT_LETRA igual a 'P'
OPTARG es 'impresora'
Ignorando los primeros $OPTIND-1 = 2 argumentos
Lo que sobró de la línea de comandos fue 'arch1 arch2'
- Dime una cosa: no podrías haber usado un
- Podría si, pero para que? Los comandos están ahí para ser usados... El ejemplo dado fue didáctico, pero imagina un programa que aceptase muchas opciones y sus parámetros podrían no estar pegados a las opciones, sus opciones también podrían o no estar pegadas, iba a ser un
- Realmente... Viéndolo de esta forma, me parece que tienes razón. Sera porque ya estoy medio cansado con tanta información nueva en mi cabeza. Vamos a tomar la del estribo o todavia quieres explicar alguna particularidad mas del Shell?
- Ni lo uno ni lo otro, yo también me cansé, pero hoy no voy a tomar la del estribo porque estoy yendo a dar clases en la UniRIO, que es la primera universidad federal del Brasil que está preparando a sus alumnos del curso de graduación en informática, en el uso del Software Libre. Pero antes te voy a dejar un problema para embarullar tu cabeza: quando tu varías el tamaño de una ventana gráfica, en el centro no aparece dinámicamente en vídeo inverso la cantidad de líneas y columnas? Entonces! Quiero que reproduzcas eso usando el lenguaje Shell.
- Mozo, traeme rapidito mi cuenta! Voy a contar hasta uno y si no me la trajiste me voy! Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar mal de los políticos lo único que tienes que hacer es mandarme un e-mail para julio.neves@gmail.com. Voy aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos que quien quiera hacer un curso nota diez de programación en Shell que mande un e-mail para julio.neves@uniriotec.br para informarse. Gracias y hasta la próxima -- HumbertoPina - 31 Jan 2007 |
|||||||||||||||||||||||||||||||||
Licencia Creative Commons - Reconocimiento y no comercial (CC) 2009 Por Visitantes del Bar de Júlio Neves. Todo el contenido de esta página puede ser usada de acuerdo a la Creative Commons License: Atribuição-UsoNãoComercial-PermanênciaDaLicença. |