Difference: TWikiBarConversa10 (1 vs. 11)

Revision 1114 Feb 2012 - MarioSilva

Line: 1 to 1
 

Conversación de Bar X


Line: 521 to 521
 Gracias y hasta la próxima

-- HumbertoPina - 31 Jan 2007

Added:
>
>

Revision 1024 Feb 2008 - CollonsTorre

Line: 1 to 1
 

Conversación de Bar X



Changed:
<
<
     -Que hay amigo, te aflojé un poco, verdad? Un ejercicio muy simple...
>
>
     -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?
Line: 14 to 14
       - Anda, enseñame lo que hiciste.
Changed:
<
<
     - Bien, además de lo que me pediste, noté 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 generase. La línea del mensaje estaría tres líneas encima del fin de la pantalla y el total de columnas sería obtenido por el comando tput cols. Mira como quedó:
>
>
     - 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
Line: 36 to 36
 tput cup $LineaMesj $Col; tput el # Borra msj de pantalla
Changed:
<
<
     - 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 las da manutención, yo siempre opto por la optimización.
>
>
     - 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.
Line: 156 to 156
 eval "$CASE"
Changed:
<
<
Parece complicado porque usé mucho el printf para formatear la pantalla, pero es bastante simple, vamos a entenderlo: el primero printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para ejecución del programa escogido. Observa sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatear la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Un chiche, cierto? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.
>
>
Parece complicado porque usé mucho el printf para formatear la pantalla, pero es bastante simple, vamos a entenderlo: el primer printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para la ejecución del programa escogido. Observa sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatear la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Una pasada, verdad? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.
  Vamos a ejecutarlo para ver la salida generada:
Line: 229 to 229
 
3 SIGQUIT   Interrupción por teclado (<CTRL+\>)
1 SIGHUP  Cuando recibe un kill -HUP
2 SIGINT  Interrupción por teclado (<CTRL+C>)
Changed:
<
<
 15   SIGTERM  Cuando recibe un kill ou kill -TERM 
>
>
 15   SIGTERM  Cuando recibe un kill o kill -TERM 
 
Changed:
<
<
Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que sería altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si el final ocurre de forma prevista, o sea si tiene un final normal, es muy fácil de hacer esta limpeza, sin embargo si el programa tiene un final brusco pueden ocurrir muchas cosas:
>
>
Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que es altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si el final ocurre de forma prevista, o sea si tiene un final normal, es muy fácil de hacer esta limpeza, sin embargo si el programa tiene un final brusco pueden ocurrir muchas cosas:
 
  • Es posible que en un determinado espacio de tiempo, el computador esté lleno de archivos de trabajo inútiles
  • El procesador podrá quedar lleno de procesos zombies y defuncts generados por procesos hijos que perdieron los procesos padres;
  • Es necesario liberar sockets abiertos para no dejar los clientes congelados;
  • Tus bancos de datos podrán quedar corruptos porque los sistemas gestores de bancos de datos necesitan de un tiempo para grabar sus buffers en disco (commit).
Changed:
<
<
En fin, existen mil razones para no usar un kill con la señal -9 y para monitorizar los fines anormales de programas.
>
>
En fin, existen mil razones para no usar un kill con la señal -9 y para monitorizar las terminaciones anormales de programas.
 

El trap no atrapa

Line: 257 to 257
  Donde los comandos cmd1, cmd2, cmdn serán ejecutados en caso de que el programa reciba las señales S1 S2 ... SN.
Changed:
<
<
Las comillas (") o los apóstrofes (') sólo son necesarias en el caso de que el trap posea más de un comando cmd asociado. Cada uno de los cmd puede también ser una función interna, una externa o otro script.
>
>
Las comillas (") o los apóstrofes (') sólo son necesarias en el caso de que el trap posea más de un comando cmd asociado. Cada uno de los cmd puede también ser una función interna, una externa u otro script.
  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 hacia una máquina remota ($RemoComp), en la cual el usuário es $Fulano, su contraseña es $Secreto y va a transmitir el archivo contenido en $Arq. Supon todavia que estas cuatro variables fueron recibidas en una rutina anterior de lectura y que este script es muy usado por diversas pesonas de la instalación. Veamos este trozo del código:
Line: 279 to 279
  Así en el caso de que hubiese una interrupción brusca (señales 1, 2, 3 o 15), antes de que el programa finalize (en el exit dentro del comando trap), o un fin normal (señal 0), el archivo /tmp/$$ seria borrado del disco.
Changed:
<
<
En el caso de que en la línea de comandos del trap no hubiese la instrucción exit, al final de la ejecución de esta línea el flujo del programa volvería al punto en que estaba cuando recibió la señal que originó la ejecución de este trap.
>
>
En el caso de que en la línea de comandos del trap no tuviese la instrucción exit, al final de la ejecución de esta línea el flujo del programa volvería al punto en que estaba cuando recibió la señal que originó la ejecución de este trap.
  Este trap podria ser subdividido, quedando de la siguiente forma:
Line: 298 to 298
  trap 'rm -f /tmp/$$ ; exit' 0 1 2 3 15
Changed:
<
<
Suponte dos casos: tu tienes dos scripts que llamaremos script1, cuya primera línea será un trap y script2, siendo este último colocado en ejecución por el primero, y por ser dos procesos diferentes, tendrán dos PID distintos.
>
>
Suponte dos casos: tu tienes dos scripts que llamaremos script1, cuya primera línea será un trap y script2, siendo este último ejecutado por una llamada del primero, y por ser dos procesos diferentes, tendrán dos PID distintos.
 
  • 1º Caso: El ftp se encuentra en script1
    En este caso, el argumento del comando trap debería estar entre comillas (") porque si ocurriese una interrupcción (<CTRL+C> o <CTRL+\>) en el script2, la línea sólo seria interpretada en este momento y el PID del script2 seria diferente del encontrado en /tmp/$$ (no te olvides que $$ es la variable que contiene el PID del proceso activo);
Line: 328 to 328
  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.
Changed:
<
<
Otra forma de restaurar una señal a su default es haciendo:
>
>
Otra forma de restaurar una señal a su patrón (default) es haciendo:
 
    trap - señal
Line: 337 to 337
 En korn shell (ksh) no existe la opción -s del comando read para leer una señal. Lo que acostumbramos hacer es usar el comando stty con la opción -echo que inhibe la escritura en pantalla hasta que se encuentre un stty echo para restaurar esta escritura. Entonces, si estamos usando el interprete ksh, la lectura de la señal sería hecha de la siguiente forma:
Changed:
<
<
echo -n "Seña: "
>
>
echo -n "Señal: "
  stty -echo
Changed:
<
<
read Seña
>
>
read Señal
  stty echo
Changed:
<
<
El problema en este tipo de construcción es que en el caso de que el operador no supiese la señal, él probablemente daría un <CTRL+C> o un <CTRL+\> durante la instrucción read para detener el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:
>
>
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 <CTRL+C> o un <CTRL+\> durante la instrucción read para detener el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:
 
Changed:
<
<
echo -n "Seña: "
>
>
echo -n "Señal: "
  trap "stty echo exit" 2 3 stty -echo
Changed:
<
<
read Seña
>
>
read Señal
  stty echo trap 2 3
Line: 361 to 361
 $ trap "echo Cambió el tamaño de la ventana " 28
Changed:
<
<
En seguida, toma el mouse (arghh!!) y arrástralo para variar el tamaño de la ventana actual. Sorprendido? Es el Shell orientado a eventos... smile
>
>
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... smile
  Uno mas, porque no me puedo resistir... Ahora escribe esto:
Line: 425 to 425
  cadenadeopciones no puede contener el signo de interrogación (?).
Changed:
<
<
El nombre constante en la línea de sintáxis anterior, define una variable que cada vez que el comando getopts sea ejecutado, recibirá la próxima opción de los parámetros de posición y la colocará en la variable nombre.
>
>
El nombre constante en la línea de sintaxis anterior, define una variable que cada vez que el comando getopts sea ejecutado, recibirá la próxima opción de los parámetros de posición y la colocará en la variable nombre.
  getopts coloca un signo de interrogación (?) en la variable definida en nombre si encuentra una opción no definida en cadenadeopciones o si no encuentra el argumento esperado para una determinada opción.
Line: 508 to 508
       - 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 case infernal y con=getopts= es sólo seguir los pasos que vimos anteriormente.
Changed:
<
<
     - 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 del Shell?
>
>
     - 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?
 
Changed:
<
<
     - Ni uno ni 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 sus alumnos del curso de graduación en informática, en el uso de Software Libre.
>
>
     - 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.

Revision 923 Feb 2008 - CollonsTorre

Line: 1 to 1
 

Conversación de Bar X



Changed:
<
<
     - Y ahí amigo, te aflojé un poco, cierto? Un ejercicio muy simple...
>
>
     -Que hay amigo, te aflojé un poco, verdad? Un ejercicio muy simple...
 
Changed:
<
<
     - Si, pero en los tests que hice y de acuerdo como lo que me enseñaste sobre substitución de parámetros, me pareció que deberia hacer otras alteraciones en las funciones que creamos, para dejarlas de uso más general como me dijiste que todas las funciones deverian de ser, quieres ver?
>
>
     - 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?
 
Changed:
<
<
     - Claro, si te pedí para hacerlas es porque estoy com ganas de verte aprender, pero alto! dame un momento!
>
>
     - Claro, si te pedí hacerlas es porque estoy con ganas de verte aprender, pero alto! dame un momento!
       - Mozo! Trae dos, uno sin espuma!
Changed:
<
<
     - Anda, mostrame lo que hiciste.
>
>
     - Anda, enseñame lo que hiciste.
 
Changed:
<
<
     - Bien, además de lo que me pediste, noté 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 - que en el caso de que una de estas variables no fuese informada, la propia función atribuiría. La línea del mensaje sería tres líneas encima del fin de la pantalla y el total de columnas sería obtenido por el comando tput cols. Ve como quedó:
>
>
     - Bien, además de lo que me pediste, noté 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 generase. La línea del mensaje estaría tres líneas encima del fin de la pantalla y el total de columnas sería obtenido por el comando tput cols. Mira como quedó:
 
$ cat pergunta.func
Line: 36 to 36
 tput cup $LineaMesj $Col; tput el # Borra msj de pantalla
Changed:
<
<
     - Me gustó, te antecipaste a lo que te iba a pedir. Solamente para encerrar esta conversa de sustitución de parámetros, nota que la legibilidad está horrible, pero la performance, o sea, la velocidad de ejecución, está óptima. Como funciones son cosas muy personales, ya que cada uno usa las suyas, y casi no se las da manutención, yo siempre opto por la performance.
>
>
     - 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 las da manutención, yo siempre opto por la optimización.
 
Changed:
<
<
     - Hoy vamos a salir de aquel aburrimiento que fue nuestra última conversa y volveremos a la lógica saliendo de la memorización, pero te vuelvo a recordar, todo lo que te mostré la otra vez aqui 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.
>
>
     - 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

Line: 49 to 49
 $ var2=var1
Changed:
<
<
     - Te dí estas dos variables, y quiero que me digas como puedo, solamente referiéndome a $var2, listar el valor de $var1 (3).
>
>
     - Te dí estas dos variables, y quiero que me digas como puedo, solamente refiriéndome a $var2, listar el valor de $var1 (3).
 
Changed:
<
<
     - Ah! eso es facil, es sólo hacer:
>
>
     - Ah! eso es fácil, es sólo hacer:
 
    echo $`echo $var2`
Changed:
<
<
     - Nota que coloqué el echo $var2 entre comillas (`), que de esta forma tendrá prioridad de ejecución y resultará en var1, montando echo$var1 que producirá 3...
>
>
     - Fíjate que coloqué el echo $var2 entre comillas (`), que de esta forma tendrá prioridad de ejecución y resultará en var1, montando echo$var1 que producirá 3...
 
Changed:
<
<
     - A sí? Entonces ejecuta a ver si está correcto.
>
>
     - A sí? Entonces ejecutalo a ver si está correcto.
 
$ echo $`echo $var2`
Line: 68 to 68
       - Eh! Que pasó? Mi razonamiento parecía bastante lógico...
Changed:
<
<
     - Tu razsonamiento realmente fue lógico, el problema es que te olvidaste de una de las primeras cosas de que te hablé aqui en el Bar y voy a repetir. El Shell usa el siguiente orden para resolver una línea de comandos:
>
>
     - 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:
 
  • Resuelve los redireccionamentos;
  • Substituye las variables por sus valores;
  • Resuelve y substituye los meta caracteres;
Line: 76 to 76
  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 $var2 y por eso tu solución produjo como salida $var1. El comando echo identificó eso como una cadena y no como una variable.
Changed:
<
<
Problemas de este tipo son relativamente frecuentes y serían insolubles en caso de que no existiese la instrucción eval, cuya sintáxis es:
>
>
Problemas de este tipo son relativamente frecuentes y serían insolubles en caso de que no existiese la instrucción eval, cuya sintaxis es:
 
    eval cmd
Changed:
<
<
Donde cmd es una línea de comando cualquiera que podría inclusive ejecutar directo en el prompt del terminal. Cuando pones el eval en la frente, lo que ocurre es que el Shell trata cmd como si sus datos fueran parámetros del eval y enseguida el eval ejecuta la línea recibida, sometiendola al Shell, dando entonces en la práctica dos pasadas en cmd.
>
>
Donde cmd es una línea de comando cualquiera que se podría inclusive ejecutar directamente en el prompt del terminal. Cuando pones el eval al principio, lo que ocurre es que el Shell trata cmd como si sus datos fueran parámetros del eval y enseguida el eval ejecuta la línea recibida, sometiéndola al Shell, dando entonces en la práctica dos pasadas en cmd.
 
Changed:
<
<
De esta forma si ejecutásemos el comando que propusiste, colocando el eval en su comienzo, tendríamos la salida esperada, ve sino:
>
>
De esta forma si ejecutásemos el comando que propusiste, colocando el eval en su comienzo, tendríamos la salida esperada, mira sino:
 
$ eval echo $`echo $var2`
Line: 98 to 98
 3
Changed:
<
<
En la primera pasada la barra invertida (\) sería retirada y $var2 resuelto, produciendo var1. Para la segunda pasada habria sobrado echo $var1, que produciría el resultado esperado.
>
>
En la primera pasada la barra invertida (\) sería retirada y $var2 resuelto, produciendo var1. Para la segunda pasada habría sobrado echo $var1, que produciría el resultado esperado.
  Ahora voy a colocar un comando dentro de var2:
Line: 127 to 127
 listamusica listartista listartista3 logado logaute.sh
Changed:
<
<
Nuevamente, al momento de la sustitución de las variables, $var1 todavia no se había presentado al Shell para ser resuelta, por eso sólo nos queda ejecutar el comando eval para dar las dos pasadas necesarias.
>
>
Nuevamente, al momento de la sustitución de las variables, $var1 todavía no se había presentado al Shell para ser resuelta, por eso sólo nos queda ejecutar el comando eval para dar las dos pasadas necesarias.
 
Changed:
<
<
Una vez un colega de una excelente lista sobre Shell Script, presentó una duda: queria hacer un menú que numerase y listase todos los archivos con extensión .sh y cuando el operador escogiese una opción, el programa correspondiente sería ejecutado. Mi propuesta fue la siguiente:
>
>
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 .sh y cuando el operador escogiese una opción, el programa correspondiente sería ejecutado. Mi propuesta fue la siguiente:
 
$ cat fazmenu
Line: 151 to 151
 CASE="$CASE *) . error;; esac"
Changed:
<
<
read -n3 -p "Informe la opción desejada: " opt
>
>
read -n3 -p "Introduce la opción deseada: " opt
 echo eval "$CASE"
Changed:
<
<
Parece complicado porque usé mucho el printf para formatear la pantalla, pero es bastante simple, vamos a entenderlo: el primero printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para ejecución del programa escogido. Nota sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatear la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Un chiche, cierto? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.
>
>
Parece complicado porque usé mucho el printf para formatear la pantalla, pero es bastante simple, vamos a entenderlo: el primero printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para ejecución del programa escogido. Observa sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatear la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Un chiche, cierto? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.
  Vamos a ejecutarlo para ver la salida generada:
Line: 175 to 175
  009 monbg.sh 010 readpipe.sh 011 redirread.sh
Changed:
<
<
Informe la opción deseada:
>
>
Introduce la opción deseada:
 
Changed:
<
<
En este programa sería interesante tener una opción de término, 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 $CASE. Veamos como quedaría:
>
>
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 $CASE. Veamos como quedaría:
 
$ cat fazmenu
Line: 202 to 202
  999) exit;; # línea alterada *) ./error;; esac"
Changed:
<
<
read -n3 -p "Informe la opción deseada: " opt
>
>
read -n3 -p "Introduce la opción deseada: " opt
 echo eval "$CASE"

Señales de Procesos

Changed:
<
<
Existe 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 en las señales mandadas para los procesos y más adelante vamos a dar una pasada rápida por las señales generados por procesos.
>
>
Existe 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.
 
Changed:
<
<

señales asesinos

>
>

señales asesinas

  Para mandar una señal a un proceso, usamos normalmente el comando kill, cuya sintáxis es:
Line: 219 to 219
  kill -sig PID
Changed:
<
<
Donde PID es el identificador del processo (Process IDentification o Process ID). Además del comando kill, algunas secuencias de teclas también pueden generar sig. La tabla a seguir muestra las señales más importantes para monitorear:
>
>
Donde PID es el identificador del processo (Process IDentification o Process ID). Además del comando kill, algunas secuencias de teclas también pueden generar sig. La tabla siguiente muestra las señales más importantes para monitorear:
 
Line: 232 to 232
 
 15   SIGTERM  Cuando recibe un kill ou kill -TERM 
Changed:
<
<
Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que sería altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si su final ocurre de forma prevista, o sea si tuviera un término normal, es muy fácil de hacer esta limpeza, sin embargo si su programa tuvo un final brusco muchas cosas pueden ocurrir:
>
>
Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que sería altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si el final ocurre de forma prevista, o sea si tiene un final normal, es muy fácil de hacer esta limpeza, sin embargo si el programa tiene un final brusco pueden ocurrir muchas cosas:
 
Changed:
<
<
  • Es posible que en un determinado espacio de tiempo, su computador esté lleno de archivos de trabajo inútiles
  • Su procesador podrá quedar lleno de procesos zombies y defuncts generados por procesos hijos que perdieron los procesos padres;
>
>
  • Es posible que en un determinado espacio de tiempo, el computador esté lleno de archivos de trabajo inútiles
  • El procesador podrá quedar lleno de procesos zombies y defuncts generados por procesos hijos que perdieron los procesos padres;
 
  • Es necesario liberar sockets abiertos para no dejar los clientes congelados;
Changed:
<
<
  • Sus bancos de datos podrán quedar corrompidos porque los sistemas gerenciadores de bancos de datos necesitan de un tiempo para grabar sus buffers en disco (commit).
>
>
  • Tus bancos de datos podrán quedar corruptos porque los sistemas gestores de bancos de datos necesitan de un tiempo para grabar sus buffers en disco (commit).
 
Changed:
<
<
En fin, existen mil razones para no usar un kill con la señal -9 y para monitorar los fines anormales de programas.
>
>
En fin, existen mil razones para no usar un kill con la señal -9 y para monitorizar los fines anormales de programas.
 

El trap no atrapa

Line: 259 to 259
  Las comillas (") o los apóstrofes (') sólo son necesarias en el caso de que el trap posea más de un comando cmd asociado. Cada uno de los cmd puede también ser una función interna, una externa o otro script.
Changed:
<
<
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 para una máquina remota ($RemoComp), en la cual el usuário es $Fulano, su contraseña es $Secreto y va a transmitir el archivo contenido en $Arq. Suponga todavia que estas cuatro variables fueron recibidas en una rutina anterior de lectura y que este script es muy usado por diversas pesonas de la instalación. Veamos este trecho del código:
>
>
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 hacia una máquina remota ($RemoComp), en la cual el usuário es $Fulano, su contraseña es $Secreto y va a transmitir el archivo contenido en $Arq. Supon todavia que estas cuatro variables fueron recibidas en una rutina anterior de lectura y que este script es muy usado por diversas pesonas de la instalación. Veamos este trozo del código:
 
ftp -ivn $RemoComp << FimFTP >> /tmp/$$ 2>> /tmp/$$
Line: 269 to 269
 FimFTP?
Changed:
<
<
Repare que, tanto las salidas de los diálogos del ftp, como los errores encontrados, están siendo redireccionados para /tmp/$$, lo que es una construcción bastante normal para archivos temporarios usados en scripts con más de un usuário, porque $$ es la variable que contiene el número de proceso (PID), que es único, y con este tipo de construcción se evita que dos o más usuários disputen la posesión y los derechos sobre el archivo.
>
>
Observa que, tanto las salidas de los diálogos del ftp, como los errores encontrados, están siendo redireccionados para /tmp/$$, lo que es una construcción bastante normal para archivos temporarios usados en scripts con más de un usuário, porque $$ es la variable que contiene el número de proceso (PID), que es único, y con este tipo de construcción se evita que dos o más usuários disputen la posesión y los derechos sobre el archivo.
 
Changed:
<
<
En el caso de que este ftp sea interrumpido por un kill o un <CTRL+C>, con seguridad que dejará basura en el disco. Es exactamente esta la forma más frecuente de usar el comando trap. Como esto es un trecho de un script, debemos hacer, luego en su comienzo y como uno de sus primeros comandos:
>
>
En el caso de que este ftp sea interrumpido por un kill o un <CTRL+C>, con toda seguridad dejará basura en el disco. Es exactamente esta la forma más frecuente de usar el comando trap. Como esto es un trozo de un script, debemos hacer, al empezar y como uno de sus primeros comandos:
 
    trap "rm -f /tmp/$$ ; exit" 0 1 2 3 15
Changed:
<
<
Así en el caso de que hubiese una interrupción brusca (señales 1, 2, 3 o 15), antes de que el programa finalize (en el exit dentro del comando trap), o un fin normal (señal 0), el archivo /tmp/$$ seíia borrado del disco.
>
>
Así en el caso de que hubiese una interrupción brusca (señales 1, 2, 3 o 15), antes de que el programa finalize (en el exit dentro del comando trap), o un fin normal (señal 0), el archivo /tmp/$$ seria borrado del disco.
  En el caso de que en la línea de comandos del trap no hubiese la instrucción exit, al final de la ejecución de esta línea el flujo del programa volvería al punto en que estaba cuando recibió la señal que originó la ejecución de este trap.
Line: 290 to 290
  Así al recibir una de las señales el programa terminaría, y al terminar, generaría una señal 0, que borraría el archivo. Si la finalización es normal, la señal también será generada y el rm será ejecutado.
Changed:
<
<
Note también que el Shell analiza la línea de comandos, una vez cuando el trap es interpretado (y es por eso que es usual colocarlo al inicio del programa) y nuevamente cuando se recibe alguna de las señales listadas. Entonces, en el último ejemplo, el valor de $$ será sustituído en el momento que el comando trap fue leído por la primera vez, ya que las comillas (") no protegen el signo de pesos ($) de la interpretación del Shell.
>
>
Observa también que el Shell analiza la línea de comandos, una vez cuando el trap es interpretado (y es por eso que es usual colocarlo al inicio del programa) y nuevamente cuando se recibe alguna de las señales listadas. Entonces, en el último ejemplo, el valor de $$ será sustituído en el momento que el comando trap es leído por primera vez, ya que las comillas (") no protegen el signo de pesos ($) de la interpretación del Shell.
 
Changed:
<
<
Si deseas que la sustitución sea realizada solamente en el momento de ser recibida la señal, el comando debería ser colocado entre apóstrofes ('). Así, en la primera interpretación del trap, el Shell no vería el signo de pesos ($), sin embargo los apóstrofes (') serian retirados y finalmente el Shell podría sustituir el valor de la variable. En este caso, la línea quedaria de la siguiente manera:
>
>
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 ('). Así, en la primera interpretación del trap, el Shell no vería el signo de pesos ($), sin embargo los apóstrofes (') serian retirados y finalmente el Shell podría sustituir el valor de la variable. En este caso, la línea quedaria de la siguiente manera:
 
    trap 'rm -f /tmp/$$ ; exit' 0 1 2 3 15
Changed:
<
<
Suponte dos casos: tu tienes dos scripts que llamaremos de script1, cuya primera línea será un trap y script2, siendo este último colocado en ejecución por el primero, y por ser dos procesos diferentes, tendrán dos PID distintos.
>
>
Suponte dos casos: tu tienes dos scripts que llamaremos script1, cuya primera línea será un trap y script2, siendo este último colocado en ejecución por el primero, y por ser dos procesos diferentes, tendrán dos PID distintos.
 
  • 1º Caso: El ftp se encuentra en script1
    En este caso, el argumento del comando trap debería estar entre comillas (") porque si ocurriese una interrupcción (<CTRL+C> o <CTRL+\>) en el script2, la línea sólo seria interpretada en este momento y el PID del script2 seria diferente del encontrado en /tmp/$$ (no te olvides que $$ es la variable que contiene el PID del proceso activo);

  • 2º Caso: El ftp anterior se encuentra en script2
    En este caso, el argumento del comando trap debería estar entre apóstrofes ('), pues en el caso de que la interrupción se diera durante la ejecución del script1, el archivo no habría sido creado, en el caso de que ocurriera durante la ejecución del script2, el valor de $$ sería el PID de este processo, que coincidiría con el de /tmp/$$.
Changed:
<
<
Cuando se ejecuta el comando trap sin argumentos, lista las señales que están siendo monitoreados en el ambiente, así como la línea de comando que será ejecutada cuando tales señales sean recibidas.
>
>
Cuando se ejecuta el comando trap sin argumentos, lista las señales que están siendo monitoreadas en el ambiente, así como la línea de comando que será ejecutada cuando tales señales sean recibidas.
  Si la línea de comandos del trap es nula (o sea vacía), esto significa que las señales especificadas deben ser ignoradas cuando sean recibidas. Por ejemplo, el comando:
Line: 312 to 312
  trap "" 2
Changed:
<
<
Especifica que la señal de interrupción (<CTRL+C>) debe ser ignorada. En ese caso no se desea que la ejecución sea interrumpida. En el último ejemplo note que el primer argumento debe ser especificado para que la señal sea ignorada, y no es equivalente a escribir lo siguiente, cuya finalidad es de retornar la señal 2 a su estado padrón (default):
>
>
Especifica que la señal de interrupción (<CTRL+C>) debe ser ignorada. En ese caso no se desea que la ejecución sea interrumpida. En el último ejemplo fíjate que el primer argumento debe ser especificado para que la señal sea ignorada, y no es equivalente a escribir lo siguiente, cuya finalidad es de retornar la señal 2 a su estado patrón (default):
 
    trap 2
Changed:
<
<
Si se ignora una señal, todos los Subshells irán a ignorar esta señal. Por lo tanto, si tu especificas cual acción debe ser tomada cuando se reciba una señal, entonces todos los Subshells irán también a tomar 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 2), significa que los Subshells serán finalizados.
>
>
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 2), significa que los Subshells serán finalizados.
  Suponte que ejecutes el comando:
Line: 334 to 334
  trap - señal
Changed:
<
<
En korn shell (ksh) no existe la opción -s del comando read para leer una seña. Lo que acostumbramos hacer es usar el comando stty con la opción -echo que inhibe la escritura en pantalla hasta que se encuentre un stty echo para restaurar esta escritura. Entonces, si estamos usando el interpretador ksh, la lectura de la seña sería hecha de la siguiente forma:
>
>
En korn shell (ksh) no existe la opción -s del comando read para leer una señal. Lo que acostumbramos hacer es usar el comando stty con la opción -echo que inhibe la escritura en pantalla hasta que se encuentre un stty echo para restaurar esta escritura. Entonces, si estamos usando el interprete ksh, la lectura de la señal sería hecha de la siguiente forma:
 
    echo -n "Seña: "
Line: 343 to 343
  stty echo
Changed:
<
<
El problema en este tipo de construcción es que en el caso de que el operador no supiese la señal, él probablemente daría un <CTRL+C> o un <CTRL+\> durante la instrucción read para descontinuar el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:
>
>
El problema en este tipo de construcción es que en el caso de que el operador no supiese la señal, él probablemente daría un <CTRL+C> o un <CTRL+\> durante la instrucción read para detener el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:
 
    echo -n "Seña: "
Line: 355 to 355
  trap 2 3
Changed:
<
<
Para terminar este asunto, abra un terminal gráfico y escriba en el prompt de comando lo siguiente:
>
>
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
Changed:
<
<
En seguida, toma el mouse (arghh!!) y arrástralo de forma de variar el tamaño de la ventana actual. Sorprendido? Es el Shell orientado a eventos... smile
>
>
En seguida, toma el mouse (arghh!!) y arrástralo para variar el tamaño de la ventana actual. Sorprendido? Es el Shell orientado a eventos... smile
 
Changed:
<
<
Uno mas, porque no pude resistir... Ahora escribe así:
>
>
Uno mas, porque no me puedo resistir... Ahora escribe esto:
 
$ trap "echo acabó" 17
Changed:
<
<
En seguida hace:
>
>
En seguida haz:
 
$ sleep 3 &
Changed:
<
<
Tu acabaste de crear un subshell que irá a dormir durante três segundos en background. Al final de este tiempo, recibirás un mensaje acabó, porque la señal 17 es emitida cada vez que un subshell termina su ejecución.
>
>
Acabas de crear un subshell que dormirá durante tres segundos en background. Al final de este tiempo, recibirás un mensaje acabó, porque la señal 17 es emitida cada vez que un subshell termina su ejecución.
 
Changed:
<
<
Para volver estas señales a sus defaults, haga:
>
>
Para volver estas señales a sus opciones por defecto, haz:
 
$ trap 17 28
Line: 399 to 399
 
  28     SIGWINCH     Cambio de tamaño de la ventana gráfica  
Changed:
<
<
Muy bueno este comando, cierto? Si tu descubres algun caso interesante de uso de señales, por favor informame por e-mail porque es muy rara la literatura sobre el asunto.
>
>
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

Changed:
<
<
El comando getopts recupera las opciones y sus argumentos de una lista de parámetros de acuerdo con la sintáxis POSIX.2, o sea, letras (o números) después de un señal de menos (-) seguidas o no de un argumento; en el caso de tener solamente letras (o números) ellas pueden ser agrupadas. Debes usar este comando para "cortar en partes" opciones y argumentos pasados para su script.
>
>
El comando getopts recupera las opciones y sus argumentos de una lista de parámetros de acuerdo con la sintáxis POSIX.2, o sea, letras (o números) después de un señal de menos (-) seguidas o no de un argumento; en el caso de tener solamente letras (o números) se pueden agrupar. Debes usar este comando para "cortar en partes" opciones y argumentos pasados hacia tu script.
  Sintáxis:
Line: 411 to 411
  getopts cadenadeopciones nombre
Changed:
<
<
La cadenadeopciones debe explicitar una cadena de caracteres con todas las opciones reconocidas por el script, así si él reconoce las opciones -a -b y -c, cadenadeopciones debe ser abc. Si deseas que una opción sea seguida por un argumento, coloca dos puntos (:) después de la letra, como en a:bc. Ésto le dice al getopts que la opción -a tiene la forma:
>
>
La cadenadeopciones debe explicitar una cadena de caracteres con todas las opciones reconocidas por el script, así si este reconoce las opciones -a -b y -c, cadenadeopciones debe ser abc. Si deseas que una opción sea seguida por un argumento, coloca dos puntos (:) después de la letra, como en a:bc. Ésto le dice al getopts que la opción -a tiene la forma:
 
    -a argumento
Line: 429 to 429
  getopts coloca un signo de interrogación (?) en la variable definida en nombre si encuentra una opción no definida en cadenadeopciones o si no encuentra el argumento esperado para una determinada opción.
Changed:
<
<
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 $1, la segunda en $2, y así continúa. Cuando el getopts obtiene una opción, él almacena el índice del próximo parámetro a ser procesado en la variable OPTIND.
>
>
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 $1, la segunda en $2, y así continúa. Cuando el getopts obtiene una opción, almacena el índice del próximo parámetro a ser procesado en la variable OPTIND.
  Cuando una opción tiene un argumento asociado (indicado por : en cadenadeopciones), getopts almacena el argumento en la variable OPTARG. Si una opción no posee argumento o el argumento esperado no se encontró, la variable OPTARG será "matada" (unset).
Changed:
<
<
El comando encierra su ejecución cuando:
>
>
El comando termina su ejecución cuando:
 
  • Encuentra un parámetro que no comienza por menos (-);
  • El parámetro especial -- marca el fin de las opciones;
  • Cuando encuentra un error (por ejemplo, una opción no reconocida).
Line: 479 to 479
  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 .
Changed:
<
<
Nota que si hubiesemos escrito la línea de comando con el argumento (impresora) separado de la opción (-P), el resultado sería exactamente el mismo, excepto por el $OPTIND, ya que en este caso él identifica un conjunto de tres opciones/argumentos y en el anterior solamente dos. Ve esto:
>
>
Fíjate que si hubiesemos escrito la línea de comando con el argumento (impresora) separado de la opción (-P), el resultado sería exactamente el mismo, excepto por el $OPTIND, ya que en este caso él identifica un conjunto de tres opciones/argumentos y en el anterior solamente dos. Mira esto:
 
$ getoptst.sh -h -P impresora arch1 arch2
Line: 491 to 491
 Lo que sobró de la línea de comandos fue 'arch1 arch2'
Changed:
<
<
En el ejemplo siguiente, ve que si pasamos una opción inválida, la variable $OPT_LETRA recibirá un signo de interrogación (?) y la $OPTARG será "matada" (unset).
>
>
En el ejemplo siguiente, fíjate que si pasamos una opción inválida, la variable $OPT_LETRA recibirá un signo de interrogación (?) y la $OPTARG será "matada" (unset).
 
$ getoptst.sh -f -Pimpresora arch1 arch2 # La opción �f no es válida
Line: 508 to 508
       - 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 case infernal y con=getopts= es sólo seguir los pasos que vimos anteriormente.
Changed:
<
<
     - Realmente... Viéndolo de esta forma, me parece que tienes razón. Es 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 del Shell?
>
>
     - 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 del Shell?
 
Changed:
<
<
     - Ni uno ni 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 universidade federal del Brasil que está preparando sus alumnos del curso de graduación en informática, en el uso de Software Libre.
>
>
     - Ni uno ni 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 sus alumnos del curso de graduación en informática, en el uso de Software Libre.
 
Changed:
<
<
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 reverso la cantidad de líneas y columnas? Entonces! Quiero que reproduzcas eso usando el lenguaje Shell.
>
>
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!
Changed:
<
<
Y no te olvides, cualquer duda o falta de compañia para tomar un chopp 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.
>
>
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

Revision 803 Feb 2008 - JulioNeves

Line: 1 to 1
Changed:
<
<

Conversa de Bar X

>
>

Conversación de Bar X

 


     - Y ahí amigo, te aflojé un poco, cierto? Un ejercicio muy simple...

Changed:
<
<
     - Si, pero en los tests que hice y de acuerdo com lo que me enseñaste sobre substitución de parámetros, me pareció que deberia hacer otras alteraciones en las funciones que creamos, para dejarlas de uso más general como me dijiste que todas las funciones deverian de ser, quieres ver?
>
>
     - Si, pero en los tests que hice y de acuerdo como lo que me enseñaste sobre substitución de parámetros, me pareció que deberia hacer otras alteraciones en las funciones que creamos, para dejarlas de uso más general como me dijiste que todas las funciones deverian de ser, quieres ver?
       - Claro, si te pedí para hacerlas es porque estoy com ganas de verte aprender, pero alto! dame un momento!

Revision 713 Feb 2007 - JulioNeves

Line: 1 to 1
 

Conversa de Bar X


Line: 518 to 518
  Y no te olvides, cualquer duda o falta de compañia para tomar un chopp 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.
Changed:
<
<
Gracias y hasta la próxima!

>
>
Gracias y hasta la próxima
  -- HumbertoPina - 31 Jan 2007

Revision 604 Feb 2007 - JulioNeves

Line: 1 to 1
 

Conversa de Bar X


Line: 403 to 403
 

Comando getopts

Changed:
<
<
El comando getopts recupera las opciones y sus argumentos de una lista de parámetros de acuerdo con la sintáxis POSIX.2, o sea, letras (o números) después de un señal de menos (-) seguidas o no de un argumento; en el caso de tener solamente letras (o números) ellas pueden ser agrupadas. Debes usar este comando para “cortar en partes" opciones y argumentos pasados para su script.
>
>
El comando getopts recupera las opciones y sus argumentos de una lista de parámetros de acuerdo con la sintáxis POSIX.2, o sea, letras (o números) después de un señal de menos (-) seguidas o no de un argumento; en el caso de tener solamente letras (o números) ellas pueden ser agrupadas. Debes usar este comando para "cortar en partes" opciones y argumentos pasados para su script.
  Sintáxis:
Line: 411 to 411
  getopts cadenadeopciones nombre
Changed:
<
<
La cadenadeopciones debe explicitar una cadena de caracteres con todas las opciones reconocidas por el script, así si él reconoce las opciones -a -b y c, cadenadeopciones debe ser abc. Si deseas que una opción sea seguida por un argumento, coloca dos puntos (:) después de la letra, como en a:bc. Ésto le dice al getopts que la opción -a tiene la forma:
>
>
La cadenadeopciones debe explicitar una cadena de caracteres con todas las opciones reconocidas por el script, así si él reconoce las opciones -a -b y -c, cadenadeopciones debe ser abc. Si deseas que una opción sea seguida por un argumento, coloca dos puntos (:) después de la letra, como en a:bc. Ésto le dice al getopts que la opción -a tiene la forma:
 
    -a argumento

Revision 503 Feb 2007 - JulioNeves

Line: 1 to 1
 

Conversa de Bar X


Line: 459 to 459
  echo "getopts hizo la variable OPT_LETRA igual a '$OPT_LETRA'" echo " OPTARG es '$OPTARG'" done
Changed:
<
<
used_up=`expr $OPTIND � 1`
>
>
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 '$*'"

Revision 403 Feb 2007 - DanielRefosco

Line: 1 to 1
 

Conversa de Bar X


Line: 66 to 66
 $var1
Changed:
<
<
     - Eh! Que pasó? Mi raciocinio parecía bastante lógico...
>
>
     - Eh! Que pasó? Mi razonamiento parecía bastante lógico...
 
Changed:
<
<
     - Tu raciocinio realmente fue lógico, el problema es que te olvidaste de una de las primeras cosas de que te hablé aqui en el Bar y voy a repetir. El Shell usa el siguiente orden para resolver una línea de comandos:
>
>
     - Tu razsonamiento realmente fue lógico, el problema es que te olvidaste de una de las primeras cosas de que te hablé aqui en el Bar y voy a repetir. El Shell usa el siguiente orden para resolver una línea de comandos:
 
  • Resuelve los redireccionamentos;
  • Substituye las variables por sus valores;
  • Resuelve y substituye los meta caracteres;
Line: 82 to 82
  eval cmd
Changed:
<
<
Donde cmd es una línea de comandocualquiera que podría inclusive ejecutar directo en el prompt del terminal. Cuando pones el eval en la frente, lo que ocurre es que el Shell trata cmd como si sus datos fueran parámetros del eval y enseguida el eval ejecuta la línea recibida, submetiendola al Shell, dando entonces en la práctica dos pasadas en cmd.
>
>
Donde cmd es una línea de comando cualquiera que podría inclusive ejecutar directo en el prompt del terminal. Cuando pones el eval en la frente, lo que ocurre es que el Shell trata cmd como si sus datos fueran parámetros del eval y enseguida el eval ejecuta la línea recibida, sometiendola al Shell, dando entonces en la práctica dos pasadas en cmd.
  De esta forma si ejecutásemos el comando que propusiste, colocando el eval en su comienzo, tendríamos la salida esperada, ve sino:
Line: 156 to 156
 eval "$CASE"
Changed:
<
<
Parece complicado porque usé mucho el printf para formatar la pantalla, pero es bastante simple, vamos a entenderlo: el primeiro printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para ejecución del programa escogido. Nota sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatar la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Un chiche, cierto? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.
>
>
Parece complicado porque usé mucho el printf para formatear la pantalla, pero es bastante simple, vamos a entenderlo: el primero printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para ejecución del programa escogido. Nota sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatear la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Un chiche, cierto? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.
  Vamos a ejecutarlo para ver la salida generada:
Line: 219 to 219
  kill -sig PID
Changed:
<
<
Donde PID es el identificador del processo (Process IDentification o Process ID). Además del comando kill, algunas secuencias de teclas también pueden generar sig. La tabla a seguir muestra las señales más importantes para monitorar:
>
>
Donde PID es el identificador del processo (Process IDentification o Process ID). Además del comando kill, algunas secuencias de teclas también pueden generar sig. La tabla a seguir muestra las señales más importantes para monitorear:
 
Line: 232 to 232
 
 15   SIGTERM  Cuando recibe un kill ou kill -TERM 
Changed:
<
<
Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que sería altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si su final ocurre de forma prevista, o sea se tuviera un término normal, es muy fácil de hacer esta limpeza, sin embargo si su programa tuvo un final brusco muchas cosas pueden ocurrir:
>
>
Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que sería altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si su final ocurre de forma prevista, o sea si tuviera un término normal, es muy fácil de hacer esta limpeza, sin embargo si su programa tuvo un final brusco muchas cosas pueden ocurrir:
 
Changed:
<
<
  • Es posible que en un determinado espacio de tempo, su computador esté lleno de archivos de trabajo inútiles
>
>
  • Es posible que en un determinado espacio de tiempo, su computador esté lleno de archivos de trabajo inútiles
 
  • Su procesador podrá quedar lleno de procesos zombies y defuncts generados por procesos hijos que perdieron los procesos padres;
  • Es necesario liberar sockets abiertos para no dejar los clientes congelados;
  • Sus bancos de datos podrán quedar corrompidos porque los sistemas gerenciadores de bancos de datos necesitan de un tiempo para grabar sus buffers en disco (commit).
Line: 259 to 259
  Las comillas (") o los apóstrofes (') sólo son necesarias en el caso de que el trap posea más de un comando cmd asociado. Cada uno de los cmd puede también ser una función interna, una externa o otro script.
Changed:
<
<
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 para una máquina remota ($RemoComp), en la cual el usuário es $Fulano, su seña es $Secreto y va a transmitir el archivo contenido en $Arq. Suponga todavia que estas cuatro variables fueron recibidas en una rutina anterior de lectura y que este script es muy usado por diversas pessoas de la instalación. Veamos este trecho del código:
>
>
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 para una máquina remota ($RemoComp), en la cual el usuário es $Fulano, su contraseña es $Secreto y va a transmitir el archivo contenido en $Arq. Suponga todavia que estas cuatro variables fueron recibidas en una rutina anterior de lectura y que este script es muy usado por diversas pesonas de la instalación. Veamos este trecho del código:
 
ftp -ivn $RemoComp << FimFTP >> /tmp/$$ 2>> /tmp/$$
Line: 271 to 271
  Repare que, tanto las salidas de los diálogos del ftp, como los errores encontrados, están siendo redireccionados para /tmp/$$, lo que es una construcción bastante normal para archivos temporarios usados en scripts con más de un usuário, porque $$ es la variable que contiene el número de proceso (PID), que es único, y con este tipo de construcción se evita que dos o más usuários disputen la posesión y los derechos sobre el archivo.
Changed:
<
<
En el caso de que este ftp sea interrumpido por un kill o un <CTRL+C>, con seguridad que dejará basura en el disco. Es exactamente esta la forma más frecuente de usar el comando trap. Como esto es un trecho de un script, debemos hacer, luego en su comienzo y como un de sus primeros comandos:
>
>
En el caso de que este ftp sea interrumpido por un kill o un <CTRL+C>, con seguridad que dejará basura en el disco. Es exactamente esta la forma más frecuente de usar el comando trap. Como esto es un trecho de un script, debemos hacer, luego en su comienzo y como uno de sus primeros comandos:
 
    trap "rm -f /tmp/$$ ; exit" 0 1 2 3 15
Line: 298 to 298
  trap 'rm -f /tmp/$$ ; exit' 0 1 2 3 15
Changed:
<
<
Supone dos casos: tu tienes dos scripts que llamaremos de script1, cuya primera línea será un trap y script2, siendo este último colocado en ejecución por el primero, y por ser dos procesos diferentes, tendrán dos PID distintos.
>
>
Suponte dos casos: tu tienes dos scripts que llamaremos de script1, cuya primera línea será un trap y script2, siendo este último colocado en ejecución por el primero, y por ser dos procesos diferentes, tendrán dos PID distintos.
 
  • 1º Caso: El ftp se encuentra en script1
    En este caso, el argumento del comando trap debería estar entre comillas (") porque si ocurriese una interrupcción (<CTRL+C> o <CTRL+\>) en el script2, la línea sólo seria interpretada en este momento y el PID del script2 seria diferente del encontrado en /tmp/$$ (no te olvides que $$ es la variable que contiene el PID del proceso activo);
Changed:
<
<
  • 2º Caso: El ftp anterior se encuentra en script2
    En este caso, el argumento del comando trap debería estar entre apóstrofes ('), pues en el caso de que la interrupción se diera durante la ejecución del script1, el archivo no tendría sido creado, en el caso de que ocurriera durante la ejecución del script2, el valor de $$ sería el PID de este processo, que coincidiría con el de /tmp/$$.
>
>
  • 2º Caso: El ftp anterior se encuentra en script2
    En este caso, el argumento del comando trap debería estar entre apóstrofes ('), pues en el caso de que la interrupción se diera durante la ejecución del script1, el archivo no habría sido creado, en el caso de que ocurriera durante la ejecución del script2, el valor de $$ sería el PID de este processo, que coincidiría con el de /tmp/$$.
 
Changed:
<
<
Cuando se ejecuta el comando trap sin argumentos, lista las señales que están siendo monitorados en el ambiente, así como la línea de comando que será ejecutada quando tales señales sean recibidas.
>
>
Cuando se ejecuta el comando trap sin argumentos, lista las señales que están siendo monitoreados en el ambiente, así como la línea de comando que será ejecutada cuando tales señales sean recibidas.
 
Changed:
<
<
Si la línea de comandos del trap es nula (o sea vacía), esto significa que las señales especificadas deven ser ignoradas cuando sean recebidas. Por ejemplo, el comando:
>
>
Si la línea de comandos del trap es nula (o sea vacía), esto significa que las señales especificadas deben ser ignoradas cuando sean recibidas. Por ejemplo, el comando:
 
    trap "" 2
Line: 320 to 320
  Si se ignora una señal, todos los Subshells irán a ignorar esta señal. Por lo tanto, si tu especificas cual acción debe ser tomada cuando se reciba una señal, entonces todos los Subshells irán también a tomar 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 2), significa que los Subshells serán finalizados.
Changed:
<
<
Supone que ejecutes el comando:
>
>
Suponte que ejecutes el comando:
 
    trap "" 2
Line: 343 to 343
  stty echo
Changed:
<
<
El problema en este tipo de construcción es que en el caso de que el operador no supiese la seña, él probablemente daría un <CTRL+C> o un <CTRL+\> durante la instrucción read para descontinuar el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:
>
>
El problema en este tipo de construcción es que en el caso de que el operador no supiese la señal, él probablemente daría un <CTRL+C> o un <CTRL+\> durante la instrucción read para descontinuar el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:
 
    echo -n "Seña: "
Line: 363 to 363
  En seguida, toma el mouse (arghh!!) y arrástralo de forma de variar el tamaño de la ventana actual. Sorprendido? Es el Shell orientado a eventos... smile
Changed:
<
<
Más uno porque no pude resistir... Ahora escribe así:
>
>
Uno mas, porque no pude resistir... Ahora escribe así:
 
$ trap "echo acabó" 17
Line: 389 to 389
 $ trap - 17 28
Changed:
<
<
Acabamos de ver otras dos señales que no so tan importante como las que vimos anteriormente, pero voy a registrarlas en la tabla a seguir:
>
>
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:
 
Line: 427 to 427
  El nombre constante en la línea de sintáxis anterior, define una variable que cada vez que el comando getopts sea ejecutado, recibirá la próxima opción de los parámetros de posición y la colocará en la variable nombre.
Changed:
<
<
getopts coloca una interrogación (?) en la variable definida en nombre si encuentra una opción no definida en cadenadeopciones o si no acha el argumento esperado para una determinada opción.
>
>
getopts coloca un signo de interrogación (?) en la variable definida en nombre si encuentra una opción no definida en cadenadeopciones o si no encuentra el argumento esperado para una determinada opción.
  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 $1, la segunda en $2, y así continúa. Cuando el getopts obtiene una opción, él almacena el índice del próximo parámetro a ser procesado en la variable OPTIND.
Line: 438 to 438
 
  • El parámetro especial -- marca el fin de las opciones;
  • Cuando encuentra un error (por ejemplo, una opción no reconocida).
Changed:
<
<
El ejemplo abajo es meramente didáctico, serviendo para mostrar, en un pequeño fragmento de código, el uso pleno del comando.
>
>
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
Line: 491 to 491
 Lo que sobró de la línea de comandos fue 'arch1 arch2'
Changed:
<
<
En el ejemplo a seguir ve que si pasamos una opción inválida, la variable $OPT_LETRA recibirá un signo de interrogación (?) y la $OPTARG será "matada" (unset).
>
>
En el ejemplo siguiente, ve que si pasamos una opción inválida, la variable $OPT_LETRA recibirá un signo de interrogación (?) y la $OPTARG será "matada" (unset).
 
$ getoptst.sh -f -Pimpresora arch1 arch2 # La opción �f no es válida
Line: 510 to 510
       - Realmente... Viéndolo de esta forma, me parece que tienes razón. Es 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 del Shell?
Changed:
<
<
     - Ni uno ni 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 universidade federal del Brasil que está preparando sus alunos del curso de graduación en informática, en el uso de Software Libre.
>
>
     - Ni uno ni 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 universidade federal del Brasil que está preparando sus alumnos del curso de graduación en informática, en el uso de Software Libre.
 
Changed:
<
<
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 reverso la cantidad de líneas y columnas? Entonces! Quiero que reproduzcas eso usando el linguaje Shell.
>
>
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 reverso 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!

Revision 302 Feb 2007 - JulioNeves

Line: 1 to 1
 

Conversa de Bar X


Line: 331 to 331
 Otra forma de restaurar una señal a su default es haciendo:
Changed:
<
<
trap � señal
>
>
trap - señal
 

En korn shell (ksh) no existe la opción -s del comando read para leer una seña. Lo que acostumbramos hacer es usar el comando stty con la opción -echo que inhibe la escritura en pantalla hasta que se encuentre un stty echo para restaurar esta escritura. Entonces, si estamos usando el interpretador ksh, la lectura de la seña sería hecha de la siguiente forma:

Line: 383 to 383
 $ trap 17 28
Changed:
<
<
O
>
>
O:
 
Changed:
<
<
$ trap � 17 28
>
>
$ trap - 17 28
 

Acabamos de ver otras dos señales que no so tan importante como las que vimos anteriormente, pero voy a registrarlas en la tabla a seguir:

Revision 202 Feb 2007 - Main.HumbertoPina

Line: 1 to 1
 

Conversa de Bar X


Line: 207 to 207
 eval "$CASE"
Added:
>
>

Señales de Procesos

Existe 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 en las señales mandadas para los procesos y más adelante vamos a dar una pasada rápida por las señales generados por procesos.

señales asesinos

Para mandar una señal a un proceso, usamos normalmente el comando kill, cuya sintáxis es:

    kill -sig PID

Donde PID es el identificador del processo (Process IDentification o Process ID). Además del comando kill, algunas secuencias de teclas también pueden generar sig. La tabla a seguir muestra las señales más importantes para monitorar:

Señales Más Importantes
 15   SIGTERM  Cuando recibe un kill ou kill -TERM 
Señal  Generado por:
0 EXIT  Fin normal de programa
1 SIGHUP  Cuando recibe un kill -HUP
2 SIGINT  Interrupción por teclado (<CTRL+C>)
3 SIGQUIT   Interrupción por teclado (<CTRL+\>)

Además de estas señales, existe el tan abusado -9 o SIGKILL que, para el proceso que lo está recibiendo, equivale a meter el dedo en el botón de apagar el computador, lo que sería altamente indeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si su final ocurre de forma prevista, o sea se tuviera un término normal, es muy fácil de hacer esta limpeza, sin embargo si su programa tuvo un final brusco muchas cosas pueden ocurrir:

  • Es posible que en un determinado espacio de tempo, su computador esté lleno de archivos de trabajo inútiles
  • Su procesador podrá quedar lleno de procesos zombies y defuncts generados por procesos hijos que perdieron los procesos padres;
  • Es necesario liberar sockets abiertos para no dejar los clientes congelados;
  • Sus bancos de datos podrán quedar corrompidos porque los sistemas gerenciadores de bancos de datos necesitan de un tiempo para grabar sus buffers en disco (commit).

En fin, existen mil razones para no usar un kill con la señal -9 y para monitorar los fines anormales de programas.

El trap no atrapa

Para hacer el control de procesos descripto antes, existe el comando trap cuya sintáxis es:

    trap "cmd1; cmd2; cmdn" S1 S2 ... SN

o

    trap 'cmd1; cmd2; cmdn' S1 S2 ... SN

Donde los comandos cmd1, cmd2, cmdn serán ejecutados en caso de que el programa reciba las señales S1 S2 ... SN.

Las comillas (") o los apóstrofes (') sólo son necesarias en el caso de que el trap posea más de un comando cmd asociado. Cada uno de los cmd puede también ser una función interna, una externa o otro script.

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 para una máquina remota ($RemoComp), en la cual el usuário es $Fulano, su seña es $Secreto y va a transmitir el archivo contenido en $Arq. Suponga todavia que estas cuatro variables fueron recibidas en una rutina anterior de lectura y que este script es muy usado por diversas pessoas de la instalación. Veamos este trecho del código:

ftp -ivn $RemoComp << FimFTP >> /tmp/$$ 2>> /tmp/$$
    user $Fulano $Secreto
    binary
    get $Arq
FimFTP

Repare que, tanto las salidas de los diálogos del ftp, como los errores encontrados, están siendo redireccionados para /tmp/$$, lo que es una construcción bastante normal para archivos temporarios usados en scripts con más de un usuário, porque $$ es la variable que contiene el número de proceso (PID), que es único, y con este tipo de construcción se evita que dos o más usuários disputen la posesión y los derechos sobre el archivo.

En el caso de que este ftp sea interrumpido por un kill o un <CTRL+C>, con seguridad que dejará basura en el disco. Es exactamente esta la forma más frecuente de usar el comando trap. Como esto es un trecho de un script, debemos hacer, luego en su comienzo y como un de sus primeros comandos:

    trap "rm -f /tmp/$$ ; exit" 0 1 2 3 15

Así en el caso de que hubiese una interrupción brusca (señales 1, 2, 3 o 15), antes de que el programa finalize (en el exit dentro del comando trap), o un fin normal (señal 0), el archivo /tmp/$$ seíia borrado del disco.

En el caso de que en la línea de comandos del trap no hubiese la instrucción exit, al final de la ejecución de esta línea el flujo del programa volvería al punto en que estaba cuando recibió la señal que originó la ejecución de este trap.

Este trap podria ser subdividido, quedando de la siguiente forma:

    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 0, que borraría el archivo. Si la finalización es normal, la señal también será generada y el rm será ejecutado.

Note también que el Shell analiza la línea de comandos, una vez cuando el trap es interpretado (y es por eso que es usual colocarlo al inicio del programa) y nuevamente cuando se recibe alguna de las señales listadas. Entonces, en el último ejemplo, el valor de $$ será sustituído en el momento que el comando trap fue leído por la primera vez, ya que las comillas (") no protegen el signo de pesos ($) de la interpretación del Shell.

Si deseas que la sustitución sea realizada solamente en el momento de ser recibida la señal, el comando debería ser colocado entre apóstrofes ('). Así, en la primera interpretación del trap, el Shell no vería el signo de pesos ($), sin embargo los apóstrofes (') serian retirados y finalmente el Shell podría sustituir el valor de la variable. En este caso, la línea quedaria de la siguiente manera:

    trap 'rm -f /tmp/$$ ; exit' 0 1 2 3 15

Supone dos casos: tu tienes dos scripts que llamaremos de script1, cuya primera línea será un trap y script2, siendo este último colocado en ejecución por el primero, y por ser dos procesos diferentes, tendrán dos PID distintos.

  • 1º Caso: El ftp se encuentra en script1
    En este caso, el argumento del comando trap debería estar entre comillas (") porque si ocurriese una interrupcción (<CTRL+C> o <CTRL+\>) en el script2, la línea sólo seria interpretada en este momento y el PID del script2 seria diferente del encontrado en /tmp/$$ (no te olvides que $$ es la variable que contiene el PID del proceso activo);

  • 2º Caso: El ftp anterior se encuentra en script2
    En este caso, el argumento del comando trap debería estar entre apóstrofes ('), pues en el caso de que la interrupción se diera durante la ejecución del script1, el archivo no tendría sido creado, en el caso de que ocurriera durante la ejecución del script2, el valor de $$ sería el PID de este processo, que coincidiría con el de /tmp/$$.

Cuando se ejecuta el comando trap sin argumentos, lista las señales que están siendo monitorados en el ambiente, así como la línea de comando que será ejecutada quando tales señales sean recibidas.

Si la línea de comandos del trap es nula (o sea vacía), esto significa que las señales especificadas deven ser ignoradas cuando sean recebidas. Por ejemplo, el comando:

    trap "" 2

Especifica que la señal de interrupción (<CTRL+C>) debe ser ignorada. En ese caso no se desea que la ejecución sea interrumpida. En el último ejemplo note que el primer argumento debe ser especificado para que la señal sea ignorada, y no es equivalente a escribir lo siguiente, cuya finalidad es de retornar la señal 2 a su estado padrón (default):

    trap 2

Si se ignora una señal, todos los Subshells irán a ignorar esta señal. Por lo tanto, si tu especificas cual acción debe ser tomada cuando se reciba una señal, entonces todos los Subshells irán también a tomar 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 2), significa que los Subshells serán finalizados.

Supone 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 default es haciendo:

    trap &#65533; señal

En korn shell (ksh) no existe la opción -s del comando read para leer una seña. Lo que acostumbramos hacer es usar el comando stty con la opción -echo que inhibe la escritura en pantalla hasta que se encuentre un stty echo para restaurar esta escritura. Entonces, si estamos usando el interpretador ksh, la lectura de la seña sería hecha de la siguiente forma:

    echo -n "Seña: "
    stty -echo
    read Seña
    stty echo

El problema en este tipo de construcción es que en el caso de que el operador no supiese la seña, él probablemente daría un <CTRL+C> o un <CTRL+\> durante la instrucción read para descontinuar el programa y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalla del terminal. Para evitar que eso pase, lo mejor a hacer es:

    echo -n "Seña: "
    trap "stty echo
          exit" 2 3
    stty -echo
    read Seña
    stty echo
    trap 2 3

Para terminar este asunto, abra un terminal gráfico y escriba en el prompt de comando lo siguiente:

$ trap "echo Cambió el tamaño de la ventana " 28

En seguida, toma el mouse (arghh!!) y arrástralo de forma de variar el tamaño de la ventana actual. Sorprendido? Es el Shell orientado a eventos... smile

Más uno porque no pude resistir... Ahora escribe así:

$ trap "echo acabó" 17

En seguida hace:

$ sleep 3 &

Tu acabaste de crear un subshell que irá a dormir durante três segundos en background. Al final de este tiempo, recibirás un mensaje acabó, porque la señal 17 es emitida cada vez que un subshell termina su ejecución.

Para volver estas señales a sus defaults, haga:

$ trap 17 28

O

$ trap � 17 28

Acabamos de ver otras dos señales que no so tan importante como las que vimos anteriormente, pero voy a registrarlas en la tabla a seguir:

Señales no Muy Importantes
  28     SIGWINCH     Cambio de tamaño de la ventana gráfica  
Señal  Generada por:
  17     SIGCHLD     Fin de un proceso hijo  

Muy bueno este comando, cierto? Si tu descubres algun caso interesante de uso de señales, por favor informame por e-mail porque es muy rara la literatura sobre el asunto.

Comando getopts

El comando getopts recupera las opciones y sus argumentos de una lista de parámetros de acuerdo con la sintáxis POSIX.2, o sea, letras (o números) después de un señal de menos (-) seguidas o no de un argumento; en el caso de tener solamente letras (o números) ellas pueden ser agrupadas. Debes usar este comando para “cortar en partes" opciones y argumentos pasados para su script.

Sintáxis:

    getopts cadenadeopciones nombre

La cadenadeopciones debe explicitar una cadena de caracteres con todas las opciones reconocidas por el script, así si él reconoce las opciones -a -b y c, cadenadeopciones debe ser abc. Si deseas que una opción sea seguida por un argumento, coloca dos puntos (:) después de la letra, como en a:bc. Ésto le dice al getopts que la opción -a tiene la forma:

    -a argumento

Normalmente uno o más espacios en blanco separan el parámetro de la opción; al mismo tiempo, getopts también manipula parámetros que vienen pegados a la opción como en:

    -aargumento

cadenadeopciones no puede contener el signo de interrogación (?).

El nombre constante en la línea de sintáxis anterior, define una variable que cada vez que el comando getopts sea ejecutado, recibirá la próxima opción de los parámetros de posición y la colocará en la variable nombre.

getopts coloca una interrogación (?) en la variable definida en nombre si encuentra una opción no definida en cadenadeopciones o si no acha el argumento esperado para una determinada opción.

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 $1, la segunda en $2, y así continúa. Cuando el getopts obtiene una opción, él almacena el índice del próximo parámetro a ser procesado en la variable OPTIND.

Cuando una opción tiene un argumento asociado (indicado por : en cadenadeopciones), getopts almacena el argumento en la variable OPTARG. Si una opción no posee argumento o el argumento esperado no se encontró, la variable OPTARG será "matada" (unset).

El comando encierra su ejecución cuando:

  • Encuentra un parámetro que no comienza por menos (-);
  • El parámetro especial -- marca el fin de las opciones;
  • Cuando encuentra un error (por ejemplo, una opción no reconocida).

El ejemplo abajo es meramente didáctico, serviendo 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 .

Nota que si hubiesemos escrito la línea de comando con el argumento (impresora) separado de la opción (-P), el resultado sería exactamente el mismo, excepto por el $OPTIND, ya que en este caso él identifica un conjunto de tres opciones/argumentos y en el anterior solamente dos. Ve esto:

$ 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 a seguir ve que si pasamos una opción inválida, la variable $OPT_LETRA recibirá un signo de interrogación (?) y la $OPTARG será "matada" (unset).

$ 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 case para evitar el getopts?

     - 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 case infernal y con=getopts= es sólo seguir los pasos que vimos anteriormente.

     - Realmente... Viéndolo de esta forma, me parece que tienes razón. Es 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 del Shell?

     - Ni uno ni 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 universidade federal del Brasil que está preparando sus alunos del curso de graduación en informática, en el uso de 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 reverso la cantidad de líneas y columnas? Entonces! Quiero que reproduzcas eso usando el linguaje 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 un chopp 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

Revision 131 Jan 2007 - Main.HumbertoPina

Line: 1 to 1
Added:
>
>

Conversa de Bar X



     - Y ahí amigo, te aflojé un poco, cierto? Un ejercicio muy simple...

     - Si, pero en los tests que hice y de acuerdo com lo que me enseñaste sobre substitución de parámetros, me pareció que deberia hacer otras alteraciones en las funciones que creamos, para dejarlas de uso más general como me dijiste que todas las funciones deverian de ser, quieres ver?

     - Claro, si te pedí para hacerlas es porque estoy com ganas de verte aprender, pero alto! dame un momento!

     - Mozo! Trae dos, uno sin espuma!

     - Anda, mostrame lo que hiciste.

     - Bien, además de lo que me pediste, noté 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 - que en el caso de que una de estas variables no fuese informada, la propia función atribuiría. La línea del mensaje sería tres líneas encima del fin de la pantalla y el total de columnas sería obtenido por el comando tput cols. Ve 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 antecipaste a lo que te iba a pedir. Solamente para encerrar esta conversa de sustitución de parámetros, nota que la legibilidad está horrible, pero la performance, o sea, la velocidad de ejecución, está óptima. Como funciones son cosas muy personales, ya que cada uno usa las suyas, y casi no se las da manutención, yo siempre opto por la performance.

     - Hoy vamos a salir de aquel aburrimiento que fue nuestra última conversa y volveremos a la lógica saliendo de la memorización, pero te vuelvo a recordar, todo lo que te mostré la otra vez aqui 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 referiéndome a $var2, listar el valor de $var1 (3).

     - Ah! eso es facil, es sólo hacer:

    echo $`echo $var2`

     - Nota que coloqué el echo $var2 entre comillas (`), que de esta forma tendrá prioridad de ejecución y resultará en var1, montando echo$var1 que producirá 3...

     - A sí? Entonces ejecuta a ver si está correcto.

$ echo $`echo $var2` $var1

     - Eh! Que pasó? Mi raciocinio parecía bastante lógico...

     - Tu raciocinio realmente fue lógico, el problema es que te olvidaste de una de las primeras cosas de que te hablé aqui en el Bar y voy a repetir. El Shell usa el siguiente orden para resolver una línea de comandos:

  • Resuelve los redireccionamentos;
  • Substituye las variables por sus valores;
  • Resuelve y substituye los meta caracteres;
  • Pasa la línea ya toda masticada, para ejecución.

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 $var2 y por eso tu solución produjo como salida $var1. El comando echo identificó eso como una cadena y no como una variable.

Problemas de este tipo son relativamente frecuentes y serían insolubles en caso de que no existiese la instrucción eval, cuya sintáxis es:

    eval cmd

Donde cmd es una línea de comandocualquiera que podría inclusive ejecutar directo en el prompt del terminal. Cuando pones el eval en la frente, lo que ocurre es que el Shell trata cmd como si sus datos fueran parámetros del eval y enseguida el eval ejecuta la línea recibida, submetiendola al Shell, dando entonces en la práctica dos pasadas en cmd.

De esta forma si ejecutásemos el comando que propusiste, colocando el eval en su comienzo, tendríamos la salida esperada, ve sino:

$ 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 (\) sería retirada y $var2 resuelto, produciendo var1. Para la segunda pasada habria sobrado echo $var1, que produciría el resultado esperado.

Ahora voy a colocar un comando dentro de var2:

$ 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 el siguiente: ls $var1; y en var1 vamos a colocar l*, veamos:

$ 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, $var1 todavia no se había presentado al Shell para ser resuelta, por eso sólo nos queda ejecutar el comando eval para dar las dos pasadas necesarias.

Una vez un colega de una excelente lista sobre Shell Script, presentó una duda: queria hacer un menú que numerase y listase todos los archivos con extensión .sh y cuando el operador escogiese una opción, el programa correspondiente sería ejecutado. Mi propuesta fue la siguiente:

$ 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 "Informe la opción desejada: " opt echo eval "$CASE"

Parece complicado porque usé mucho el printf para formatar la pantalla, pero es bastante simple, vamos a entenderlo: el primeiro printf fue colocado para hacer el encabezado y en seguida comencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval para ejecución del programa escogido. Nota sin embargo que dentro del loop del for existen dos printf: el primero sirve para formatar la pantalla y el segundo para montar el case (si antes del comando read colocas una línea echo "$CASE", verás que el comando case montado dentro de la variable está todo indentado. Un chiche, cierto? :). En la salida del for, fue agregada una línea a la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada una función externa para dar mensajes de error.

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 Informe la opción deseada:

En este programa sería interesante tener una opción de término, 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 $CASE. Veamos como quedaría:

$ 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 "Informe la opción deseada: " opt echo eval "$CASE"

-- HumbertoPina - 31 Jan 2007

 
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki-SL? Send feedback