123
\ Aqui temos um livro livre e completo sobre Shell Os sedentos do "saber livre" são muito benvindos. [ 09 Feb 2014 - 06:45 ] Links Amigos BR-Linux Viva o Linux Dicas-L Aurelio Thobias 1. TWiki Básico Página Inicial Últimas Alterações Índice Procura Estatísticas de Uso Aviso de Atualização Mapa do Site 2. TWiki Avançado Registre-se Configurações Gerais Quem Somos Regras de Formatação Biblioteca Gráfica Carinhas Gráficas 3. Projeto Gráfico Pré Tópico Pós Tópico Menu Lateral Menu de Navegação CSS Utilizado Botões Especiais Indica Onde Estamos Cabeçalho Padrão Copy Right/Left Você está aqui: TWikiBar > TWikiBarConversa001 Controles: - Última Atualização: [14 Nov 2013 - V.26] Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux El Ambiente Shell Una visión rápida em los Principales Sabores de Shell Bourne Shell (sh) Korn Shell (ksh) Boune Again Shell (bash) C Shell (csh) Explicando el funcionamento de Shell Exámen de la Línea de Comandos .:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14 http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 1 / 123

Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa001Controles: - Última Atualização: [14 Nov 2013 - V.26]

Conversación de Bar - Parte IDiálogo escuchado entre un Linuxer y un empujador de mouse:El Ambiente LinuxEl Ambiente Shell

Una visión rápida em los Principales Sabores de ShellBourne Shell (sh)Korn Shell (ksh)Boune Again Shell (bash)C Shell (csh)

Explicando el funcionamento de ShellExámen de la Línea de Comandos

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 1 / 123

Page 2: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

AsignaciónComando

Resolución de RedireccionamentosSubstitución de variablesSubstitución de Meta caracteresPasa línea de Comando para el kernel

Descifrando la Piedra de Rosetacaracteres que cambian el significado

Apóstrofe o plic (')Contrabarra o Barra Invertida (\)Aspas (")

caracteres de redireccionamentoRedireccionamento de la Salida PatrónRedireccionamento de la Salida de Error PatrónRedireccionamento de la Entrada Patrón

Redireccionamento de ComandosCaracteres de Ambiente

Diálogo escuchado entre un Linuxer y un empujador demouse: - Quién es el Bash?

- El Bash es el hijo mas nuevo de la familia Shell.

- Espera ahí! Quieres volverme loco? Tenía una duda y ahora me dejas con dos!

- No, loco ya lo eras antes de aparecer por aqui. Desde que decidiste usar aquél sistemaoperativo con el cual tienes que reiniciar tu máquina unas diez veces por dia y no tienes dominioninguno sobre lo que está pasando en el computador. Pero deja eso de lado, te voy a explicar loque es el Shell y los componentes de su familia y al final de la explicación me dirás: "Mi Dios delShell! Porque no opté antes por Linux?".

El Ambiente LinuxPara que entiendas lo que es y como funciona el Shell, primero te mostraré como funciona elambiente en capas de Linux. Da una mirada atenta en el gráfico que sigue:

En este gráfico se ve que la capa de hardware es la mas profunda estando formada por loscomponentes físicos de tu computador. Envolviendo a ésta, viene la capa del kernel que es elcorazón de Linux, su núcleo, y es quien hace que el hardware funcione, efectuando su manejo ycontrol. Los programas y comandos que envuelven el kernel, lo utilizan para realizar las tareasespecificas para las cuales fueron desarrolladas. Encerrando todo eso viene el Shell que tieneeste nombre porque en ingles, Shell significa concha, envoltura, o sea que, queda entre losusuarios y el sistema operativo, de forma que todo lo que interacciona con el sistema operativo,tiene que pasar por su filtro.

El Ambiente ShellBueno, si para llegar al núcleo de Linux, o sea al kernel que es lo que le interesa a cualquieraplicacion, es necesario el filtro del Shell, vamos entonces a entender como funciona este, y laforma de sacar el mayor provecho de las innúmerables facilidades que él nos ofrece.

El Linux por definición es un sistema multiusuario - no podemos nunca olvidar ésto – y para

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 2 / 123

Page 3: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

permitir el acesso de determinados usuarios e impedir la entrada de otros, existe un archivollamado /etc/passwd que además de proveer datos para esta función, especie de "guardián depuerta" del Linux, también pasa información para el login de aquellos que consiguieron pasar poresta primera barrera. El último campo de sus registros, informa al sistema cual es el_Shell_ que lapersona va a recibir cuando se "loguee" (Ajjjjj!!!). Sewa Mobil Jakarta | GPS tracking | parfum

Cuando dije que el último campo del /etc/passwd informa al sistema cual es el Shell que elusuario va a recibir al "loguearse", es para ser interpretado literalmente, o sea, si en este campode su registro está prog, la persona al acceder al sistema recibirá la pantalla de ejecución delprograma prog y al terminar la ejecución saldrá inmediatamente con un logout. Imagina cuantopodemos aumentar la seguridad con este simples truco.

Te acuerdas que te mencioné de la familia Shell? Exactamente, vamos a comenzar a entenderesto: el Shell, que se vale de la imagen de una concha envolviendo el sistema operativopropiamente dicho, es el nombre genérico para tratar los hijos de esta idea que, con el correr delos años de existencia del sistema operativo Unix fueron apareciendo. Actualmente existendiversos “sabores” de Shell, entre ellos destaco el sh (Bourne Shell), el ksh (Korn Shell), bash(Bourne Again Shell) y el csh (C Shell).

Una visión rápida em los Principales Sabores de Shell

Bourne Shell (sh)

Desarrollado por Stephen Bourne de la Bell Labs (de AT&T donde también fue desarrollado elUnix), este fue durante muchos años el Shell patrón del sistema operativo Unix. Es tambiénllamado de Standard Shell por haber sido durante varios años, el único y hasta hoy es el masutilizado ya que fue transportado para todos los ambientes Unix y distros Linux.

Korn Shell (ksh)

Desarrollado por David Korn, también de la Bell Labs, es un superconjunto del sh, o sea, poseetodas las facilidades del sh y a ellas se agregaron muchas otras. La compatibilidade total con el shesta atrayendo a muchos usuarios y programadores de Shell para este ambiente.

Boune Again Shell (bash)

Este es el Shell mas moderno y cuyo número de adeptos crece mas en todo el mundo, sea por serel Shell default de Linux, su sistema operativo natural, o sea por su gran diversidad de comandos,que incorpora inclusive diversas instrucciones características del C Shell.

C Shell (csh)

Desarrollado por Bill Joy de la Berkley University es el Shell mas utilizado en ambientes *BSD eXenix. La estrutura de sus comandos es bastante similar al del lenguage C. Su gran pecado fueignorar la compatibilidad con el sh, partiendo por un camino propio.

Además de estos Shells existen otros, pero contigo voy a hablar solamente sobre los tresprimeros, tratandolos genéricamente por Shell y señalando las peculiaridades de cada uno queeventualmente tengan.

Explicando el funcionamento de Shell

El Shell es el primer programa al que accedes al "loguearte" en Linux. Es él quien va a resolveruna cantidad de cosas para no cargar al kernel con tareas repetitivas, aliviandolo para tratarasuntos mas importantes. Como cada usuario posee su propio Shell interponiendose entre él y el

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 3 / 123

Page 4: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Linux, es el Shell quien interpreta los comandos que son tecleados y examina sus sintaxis,pasándolos desmenuzados para su ejecución.

- Alto ahí, no corra tanto! Ese trabajo de interpretar comandos no tiene nada que ver conintérprete, no es cierto?

- Tiene que ver si, la verdad el Shell es un interpretador (o intérprete realmente) que trae consigoun poderoso lenguaje con comandos de alto nivel, que permite la construcción de loops (lazos),tomas de decisión y de almacenamiento de valores en variables, como te voy mostrar ahora.

Voy a explicarte las principales tareas que el Shell cumple, por su orden de ejecución. Prestalemucha atención a este orden porque es fundamental para comprender el resto de nuestraconversación.

Exámen de la Línea de Comandos

En este exámen el Shell identifica los caracteres especiales (reservados) que tienen significadopara la interpretación de la línea, inmediatamente después verifica si la línea pasada es unaasignación o un comando.

Asignación

Si el Shell encuentra dos campos separados por un símbolo de igual (=) sin espacios en blancoentre ellos, identifica esta secuencia como una asignación.

Exemplos

$ ls linux linux

En este ejemplo el Shell identificó el ls como un programa y el linux como un parámetro pasadopara el programa ls.

$ valor=1000

En este caso, por no haber espacios en blanco (y así identificamos que el espacio en blanco es unde los caracteres reservados) el Shell identificó una asignación y colocó 1000 en la variable valor.

Nunca Haga:

$ valor = 1000 bash: valor: not found

Aqui el Bash tomo la palabra valor aislada por espacios en blanco y juzgó que tu habías mandadoejecutar un programa llamado valor, al cual estarías pasando dos parámetros: = e 1000.

Comando

Cuando se escribe una línea en el prompt de Linux, esta es dividida en pedazos separados porespacios en blanco: el primer pedazo es el nombre del programa y su existencia seracomprobada; identifica en seguida, en este orden: opciones/parámetros, redireccionamentos yvariables.

Si el programa identificado existe, el Shell verifica los permisos de los archivos involucrados(inclusive el propio programa), dando un señal de error en caso de que tu no estés autorizado aejecutar esta tarea.

Resolución de Redireccionamentos

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 4 / 123

Page 5: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Después de identificar los componentes de la línea que tecleaste, el Shell parte para la resoluciónde redireccionamentos. El Shell tiene incorporado a su elenco de ventajas lo que llamamos elredireccionamento, que puede ser de entrada (stdin), de salida (stdout) o de errores (stderr), deacuerdo a como te explicaré a continuación.

Substitución de variables

En este punto, el Shell verifica si las eventuales variables (parámetros comenzados por $),encontradas en el campo del comando, están definidas y las substituye por sus valores actuales.

Substitución de Meta caracteres

Si algún metacaracter (*, ? ou []) es hallado en la línea de comando, es aquí que será substituidopor sus posibles valores. Suponiendo que el único archivo que comienza por la letra n en su actualdirectorio sea un directorio llamado nombregrandeparacaramba, si tu haces:

$ cd n*

Como hasta aquí quien está trabajando su línea es el Shell y el comando (programa) cd todavía nofue ejecutado, el Shell transforma el n* en nombregrandeparacaramba y el comando cd seráejecutado con éxito.

Pasa línea de Comando para el kernel

Completadas las anteriores tareas, el Shell monta la línea de comandos, ya con todas lassubstituciones hechas, llama el kernel para ejecutarla en un nuevo Shell (Shell hijo), recibiendo unnúmero de proceso (PID o Process IDentification) y permanece inactivo, durmiendo una siestecita,durante la ejecución del programa. Una vez finalizado este proceso (junto con el Shell hijo), recibenuevamente el control y, exhibiendo un prompt, muestra que está listo para ejecutar otroscomandos.

Descifrando la Piedra de Roseta

Para sacarte aquella sensación que tienes cuando ves un script Shell, que mas parece una sopade letras o un jeroglífico, te mostraré los principales caracteres especiales para que puedas salirpor ahí como el Jean-François Champollion descifrando la Piedra de Roseta (vale la pena daruna "googlada" para descubrir quién es este tipo).

caracteres que cambian el significado

Por eso mismo, cuando deseamos que el Shell no interprete un carácter especial, debemos"esconderlo" de él. Eso puede hacerse de tres formas distintas:

Apóstrofe o plic (')

Cuando el Shell ve una cadena de caracteres entre apostrofes ('), él saca los apostrofes de lacadena y no interpreta su contenido.

$ ls linux* linuxmagazine $ ls 'linux*' bash: linux* no such file or directory

En el primer caso el Shell "abrió" el asterisco y descubrió el archivo linuxmagazine para listar. Enel segundo, los apostrofes evitaron la interpretación del Shell y dio la respuesta que no existe elarchivo linux*.

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 5 / 123

Page 6: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Contrabarra o Barra Invertida (\)

Idéntico a los apostrofes excepto que la barra invertida evita la interpretación del carácter que lasigue solamente.

Suponga que accidentalmente has creado un archivo llamado * (asterisco) – que algunos saboresde Unix permiten - y deseas eliminarlo. Si tu hicieras:

$ rm *

Tendrías un gran problema, ya que el rm borraría todos los archivos del directorio corriente. Lamejor forma de hacerlo es:

$ rm \*

De esta forma, el Shell no interpretaría el asterisco, y por consiguiente no haría su abertura

Haz la siguiente experiencia científica:

$ cd /etc $ echo '*' $ echo \* $ echo *

Viste la diferencia? Entonces no se precisa explicar mas.

Aspas (")

Exactamente igual a apostrofe excepto que, si la cadena entre aspas contiene un signo de pesos($), un acento invertido (`), o una barra invertida (\), estos caracteres serán interpretados por elShell.

No debes preocuparte ahora con todo eso, todavía no te di ejemplos del uso de las aspas porquetodavía no conoces el signo de pesos ($) ni el acento grave (`). De aquí en adelante veremos conmucho detalle el uso de estos caracteres especiales, lo mas importante es entender el significadode cada uno.

caracteres de redireccionamento

La mayoría de los comandos tiene una entrada, una salida y puede generar errores. Esta entradaes llamada Entrada Patrón o stdin y su default es el teclado del terminal. Análogamente, la salidadel comando es llamada Salida Patrón o stdout y su default es la pantalla del terminal. Hacia lapantalla también son enviados por default los mensajes de error oriundos del comando, y que eneste caso es la llamada Salida de Error Patrón o stderr. Veremos ahora como alterar este estadode cosas.

Vamos a hacer un programa tartamudo. Para eso haz:

$ cat

El cat es una instrucción que lista el contenido del archivo especificado para la Salida Patrón(stdout). En el caso que la entrada no esté definida, el espera los datos de la stdin. Mira, comoyo no especifiqué la entrada, el está esperándola por el teclado (Entrada Patrón) y como tampocono cité la salida, lo que yo escriba irá hacia la pantalla (Salida Patrón) haciendo de esta forma,como había propuesto un programa tartamudo. Prueba, prueba, el teclado no muerde!

Redireccionamento de la Salida Patrón

Para especificar una salida de programa usamos el > (mayor que) o el >> (mayor, mayor) seguido

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 6 / 123

Page 7: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

del nombre del archivo al que se desea mandar la salida.

Vamos a transformar el programa tartamudo en un editor de textos (que pretensión eh!).

$ cat > Arch

El cat continua sin tener una entrada especificada, por lo tanto está aguardando que los datossean escritos, sin embargo su salida está siendo desviada hacia el archivo Arch. De esta forma,todo lo que esta siendo escrito está yendo hacia Arch, de forma que hicimos el editor de textosmas corto y pobre del planeta.

Si ejecutas nuevamente:

$ cat > Arch

Los datos contenidos en Arch se perderán, ya que antes del redireccionamento el Shell creará unArch vacio. Para colocar mas informaciones en el final del archivo deberias haber hecho:

$ cat >> Arch

Como ya te habia dicho, el Shell resuelve la linea y despues manda el comando para su ejecución.Asi, si tu redireccionas la salida de un archivo hacia el mismo, primero el Shell "vacia" estearchivo y después manda el comando para su ejecución, y de esta forma acabas de perder elcontenido de tu querido archivo.

Con esto notamos que el >> (mayor mayor) sirve para incluir texto al final del archivo.

Redireccionamento de la Salida de Error Patrón

Así como el default del Shell es recibir los datos del teclado y mandar las salidas hacia la pantalla,los errores también serán enviados hacia la pantalla si tu no especificas hacia donde deverán serenviados. Para redireccionar los errores usa 2> SalidaDeError. Fijate que entre el número 2 y elsímbolo de mayor (>) no existe espacio en blanco.

Presta atención! No confundas >> con 2>. El primero anexa datos al final de un archivo, en cuantoel segundo redirecciona la Salida de Error Patrón (stderr) hacia el archivo que ha sido designado.Esto es importante!

Suponte que durante la ejecución de un script tu puedes, o no (dependiendo del rumbo tomado porla ejecución del programa), haber creado un archivo llamado /tmp/seraqueexiste$$. Para no dejarbasura en tu disco, al final del script deberías colocar esta línea:

$ rm /tmp/seraqueexiste$$

En caso de no existir el archivo, seria enviado hacia la pantalla un mensaje de error. Para que esono ocurra se debe hacer:

$ rm /tmp/seraqueexiste$$ 2> /dev/null

Sobre el ejemplo que acabamos de ver tengo dos consejos a dar:

Consejo # 1

El $$ contiene el PID, o sea, el número de su proceso. Como Linux es multiusuario, es de buenosmodales anexar siempre el $$ a los nombre de los archivos que serán usados por varias personaspara que no haya problemas de propriedad, o sea, en caso que nombrases tu archivosimplemente como seraqueexiste, el primero que lo usase (creándolo entonces) seria su dueño y

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 7 / 123

Page 8: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

todos los otros obtendrian un error cuando intentasen grabar algo en él.

Para que hagas un test de la Salida de Error Patrón te voy a dar otro ejemplo, escribedirectamente en el prompt del Shell:

$ ls noexiste bash: noexiste no such file or directory $ ls noexiste 2> archivodeerrores $ $ catarchivodeerrores bash: noexiste no such file or directory

En este ejemplo, vimos que cuando hicimos un ls en noexiste, obtuvimos un mensaje de error.Después, redireccionamos la Salida de Error Patrón hacia archivodeerros y executamos elmismo comando, recibiendo solamente el prompt en la pantalla. Cuando listamos el contenido delarchivo para el cual fue redireccionada la Salida de Error Patron, vimos que el mensaje de errorhabia sido almacenado en él. Haz este test.

Dica # 2

- Quién es ese tal de /dev/null?

- En Unix existe un archivo fantasma. Llamase /dev/null. Todo lo que es enviado a este archivodesaparece. Se parece a un Agujero Negro. En el caso del ejemplo, como no me interesabaguardar el posible mensaje de error proveniente del comando rm, lo redireccioné para estearchivo.

Es interesante notar que estos caracteres de redireccionamento son acumulativos, o sea, si en elejemplo anterior hicieramos:

$ ls noexiste 2>> archivodeerrores

el mensaje de error proveniente del ls seria añadido al final de archivodeerrores.

Redireccionamento de la Entrada Patrón

Para hacer el redireccionamento de la Entrada Patrón usamos el < (menor que).

- Y para que sirve eso? - me vas a preguntar.

- Déjame darte un ejemplo que vas a entender rapidito.

Supone que quieres mandar un mail a tu jefe. Para el Jefe queremos lo mejor, no es así?entonces, en lugar de escribir el mail dirigiéndolo directamente al prompt de la pantalla de formade que será imposible la corrección de una frase anterior donde, sin querer, escribiste un"nosotros va", tu editas un archivo con el contenido del mensaje y después de unas quinceverificaciones sin constatar errores, decides enviarlo y para eso haces:

$ mail jefe < archivoconmailparaeljefe

Tu jefe reciberá entonces el contenido del archivoconmailparaeljefe.

Otro tipo de redireccionamento muy loco que el Shell te permite es el llamado here document. Esrepresentado por << (menor menor) y sirve para indicar al Shell que el alcance de un comandocomenza en la línea siguiente y termina cuando encuentra una línea cuyo contenido sea unicamentela etiqueta que sigue al símbolo <<.

Observa a continuación el fragmento de un script, con una rutina de ftp:

ftp -ivn hostremoto << fimftp user $Usuario $Seña binary

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 8 / 123

Page 9: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

get archivoremotofimftp

En este pedacito de programa tenemos una cantidad de detalles interesantes:

Las opciones que usé para el ftp (-ivn) sirven para ir listando todo lo que está ocurriendo (—v de verbose), para no preguntar si tienes la seguridad de que deseas transmitir cadaarchivo (—i de interactive), y finalmente la opción —n sirve para decirle al ftp para que nosolicite el usuario y su contraseña, pues estos estarán indicados por la instrucción específica(user);Cuando usé el << fimftp, estaba diciendo lo siguiente para el intérprete:- Mira aqui Shell, no hagas nada a partir de aqui hasta encontrar la etiqueta fimftp. Tu noentenderias nada, ya que son instrucciones específicas del comando ftp y tu no entiendiesnada de ftp.Si solo fuera eso, seria simple, pero el mismo ejemplo dá para ver que existen dos variables($Usuário e $Senha), que el Shell va a resolver antes del redireccionamento. Sin embargo, lagran ventaja de este tipo de construcción es que ella permite que los comandos tambiénsean interpretados dentro del alcance del here document, lo que también contradice lo queacabe de decir. Enseguida explico como funciona esto. No es este el momento todavía, nosfaltan herramientas.El comando user es del repertorio de instrucciones del ftp y sirve para pasar el usuario y lacontraseña, que habian sido leídos en una rutina anterior a ese fragmento de código ycolocados respectivamente en las dos variables: $Usuário y $Seña.El binary es otra instrucción del ftp, que sirve para indicar que la transferencia dearchivoremoto será hecha en modo binario, o sea, el contenido del archivo no seráinterpretado para saber se está en ASCII, EBCDIC, ...El get archivoremoto le dice al ftp que saque ese archivo del hostremoto y lo traiga anuestro host local. Si fuera para mandar el archivo, usariamos el comando put.

Un error muy frecuente en el uso de labels (como el fimftp del ejemplo anterior) es causado porla presencia de espacios en blanco antes o después del mismo. Estate muy atento en relación aesto, por que este tipo de errores causan muchos dolores de cabeza a los programadores, hastaque son detectados. Acuérdate: un label que se precie, tiene que tener una linea enteritasolamente para él.

- Está bien, está bien! Ya sé que salí de viaje y entré por los comandos del ftp, escapando denuestro asunto, que es el Shell, pero como siempre es bueno aprender algo mas y es raro que laspersonas estén dispuestas para enseñar...

Redireccionamento de Comandos

Los redireccionamentos que hablamos hasta aqui siempre se referían a archivos, o sea mandabanhacia archivo, recibían de archivo, simulaban archivo local, ... Lo que veremos a partir de ahora,redirecciona la salida de un comando hacia la entrada de otro. Esto es utilísimo y facilita la vida unmontón. Su nome es pipe (que en inglés significa tubo, ya que él envía la salida de un comandodirectamente como por un caño hacia la entrada del otro) y su representación es una barra vertical(|).

$ ls | wc -l 21

El comando ls pasó la lista de archivos para el comando wc, que cuando está con la opción –lcuenta la cantidad de lineas que recibió. De esta forma, podemos afirmar categóricamente que enmi diretorio existían 21 archivos.

$ cat /etc/passwd |sort | lp

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 9 / 123

Page 10: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Esta linea de comandos manda la lista del archivo /etc/passwd hacia la entrada del comandosort. Éste la clasifica y la manda para el lp que es el gerente mandamás del spool de impresión.

Caracteres de Ambiente

Cuando quieres priorizar una expresión la colocas entre paréntesis, no es así? Puede ser, a causade la aritmética es normal que pensemos así. Sin embargo, en Shell lo que prioriza realmente sonlos acentos graves (`) y no los paréntesis. Te voy a dar ejemplos del uso de los acentos gravespara que lo entiendas mejor.

Si quiero saber cuántos usuarios están "logados" en el computador que administro. Puedo hacer:

$ who | wc -l 8

El comando who pasa la lista de usuarios conectados al comando wc –l que cuenta cuantas lineasrecibió y lista la respuesta en la pantalla. Ahora bien, en lugar de tener un ocho suelto en lapantalla, lo que quiero es que esté colocado en medio de una frase.

Entonces, para mandar frases hacia la pantalla usamos el comando echo, veamos entonces comoqueda:

$ echo "Existen who | wc -l usuarios conectados" Existen who | wc -l usuarios conectados

Epa! Que pasó?, no funcionó! Exactamente, no funcionó y no fue por causa de las aspas quecoloqué, sino porque tendria que haber ejecutado el who | wc -l antes del echo. Para resolvereste problema, tengo que priorizar esta segunda parte del comando com el uso de los acentosgraves, haciéndolo de la siguiente forma:

$ echo "Existen `who | wc -l` usuarios conectados" Existen 8 usuarios conectados

Para eliminar esa cantidad de espacios en blancos antes del 8 que el wc -l produjo, basta sacarlas aspas. Así:

$ echo Existen `who | wc -l` usuarios conectados Existen 8 usuarios conectados

Como dije antes, las aspas protejen todo lo que está dentro de sus límites, de la interpretación delShell. Como para el Shell basta un espacio en blanco como separador, esa cantidad de espaciosserá cambiada por un único espacio, después de haber retirado las aspas (económico, eh?).

Antes de hablar sobre el uso de los paréntesis déjame explicar rapidamente acerca del uso delpunto y coma (;). Cuando estés en el Shell, siempre debes dar un comando en cada linea. Paraagrupar comandos en una misma línea tenemos que separarlos por punto y coma. De esta forma:

$ pwd ; cd /etc; pwd; cd -; pwd /home/midir /etc/ /home/midir

En este ejemplo, listé el nombre del directorio corriente con el comando pwd, me cambié para eldirectorio /etc, nuevamente listé el nombre del directorio y finalmente volví para el directorio dondeestaba anteriormente (cd -), listando su nombre. Note que coloqué el punto y coma (;) de todaslas formas posibles para mostrar que no importa si existen espacios en blanco antes o despuésde este caracter.

Finalmente veamos el caso de los paréntesis. Observa el caso siguiente, muy parecido al ejemploanterior:

$ (pwd ; cd /etc ; pwd;) /home/midir /etc/ $ pwd /home/midir

- Qué es eso, por amor del Shell!? Yo estaba en /home/midir, me cambié para el /etc, verifiqué

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 10 / 123

Page 11: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

que estaba en ese directorio con el pwd seguiente y cuando el agrupamiento de comandosterminó, vi que continuaba en el /home/midir, como si nunca hubiera salido de alli!

- Ah! Será que es cosa de magos?

- No me confundas, amigo!! No es nada de eso! Lo interesante del uso de paréntesis es que sellama a un nuevo Shell para ejecutar los comandos que están en su interior. De esta forma,realmente fuimos para el directorio /etc, sin embargo cuando todos los comandos dentro de losparéntesis fueron ejecutados, el nuevo Shell que estaba en el directorio /etc murió y volvimos alShell anterior cuyo directorio corriente era /home/midir. Haz otras pruebas usando cd, y ls paraque dejes este concepto bien cimentado.

Ahora que ya conocemos estos conceptos observemos este ejemplo que sigue:

$ mail apoyo << FIM > Hola apoyo, hoy a las ‘date "+%H:%M"‘ > ocurrió nuevamente aquelproblema > que ya habia informado por > teléfono. De acuerdo con su pedido > ahí va una lista delos archivos > del directorio: > ‘ls —l‘ > Abrazos a todos. > FIM

Finalmente ahora tenemos el conocimiento necesario para mostrar lo que habiamos conversadosobre here document. Los comandos entre acentos graves (`) tendrán prioridad y por tanto elShell los ejecutará antes de la instrucción mail. Cuando el apoyo reciba el e-mail, verá que loscomandos date y ls fueron ejecutados inmediatamente antes del comando mail, recibiendoentonces una fotografía del ambiente en el momento en que la correspondencia fue enviada.

El prompt primario default del Shell, como vimos, es el símbolo de pesos ($), sin embargo el Shellusa el concepto de prompt secundario, o de continuación de comando, que es enviado para lapantalla cuando hay un quiebro de linea y la instrucción no terminó. Ese prompt, es representadopor un símbolo de mayor (>), que vemos precediendo a partir de la 2ª linea del ejemplo.

Para finalizar y mezclarlo todo, debo decir que existe una construcción mas moderna que vienesiendo utilizada como forma de priorizar la ejecución de comandos, como hacen los acentosgraves (`). Son las construcciones del tipo $(cmd), donde cmd es un (o varios) comando(s) queserá(n) ejecutado(s) con prioridad en su contexto.

De esta forma, el uso de acentos graves(`) o construcciones del tipo $(cmd) sirven para el mismofin, sin embargo para quien trabaja con sistemas operativos de diversos fabricantes(multiplataforma), aconsejo el uso de los acentos invertidos, ya que el $(cmd) no fue trasladadopara todos los sabores de Shell. Aqui dentro del Bar, usaré las dos formas, indistintamente.

Veamos nuevamente el ejemplo dado para los acentos invertidos, ahora con una nueva visión:

$ echo Existen $(who | grep wc -l) usuarios conectados Existen 8 usuarios conectados

Mira este caso:

$ Archs=ls $ echo $Archs ls

En este ejemplo, hice una asignación (=) y ejecuté una instrucción. Lo que quería era que lavariable $Archs, recibiese la salida del comando ls. Como las instrucciones de un script soninterpretadas de arriba hacia abajo y de izquierda a derecha, la asignación fue hecha antes de laejecución del ls. Para hacer lo que deseamos es necesario que le dé prioridad a la ejecución deeste comando en perjuicio de la asignación y esto puede ser realizado de cualquiera de lasmaneras siguientes:

$ Archs=`ls`

o:

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 11 / 123

Page 12: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ Archs=$(ls)

Para cerrar este asunto con broche de oro, vamos a ver un último ejemplo. Digamos que quierocolocar dentro de la variable $Archs la lista detallada (ls -l) de todos los archivos comenzadospor arch y seguidos de un único caracter (?). Para eso deberia hacer:

$ Archs=$(ls -l arch?)

o:

$ Achs=`ls -l arch?`

Sin embargo, ve esto:

$ echo $Archs -rw-r--r-- 1 jneves jneves 19 May 24 19:41 arch1 -rw-r--r-- 1 jneves jneves 23 May 2419:43 arch2 -rw-r--r-- 1 jneves jneves 1866 Jan 22 2003 archl

- Caramba! salió todo junto!

- Eso mismo, como ya te dije, si tu dejas que el Shell “vea” los espacios en blanco, siempre quehaya diversos espacios juntos, estos serán cambiados por uno solo. Para que la lista salga bienbonita, es necesario proteger la variable de la interpretación de Shell, así:

$ echo "$Archs" -rw-r--r-- 1 jneves jneves 19 May 24 19:41 arch1 -rw-r--r-- 1 jneves jneves 23 May24 19:43 arch2 -rw-r--r-- 1 jneves jneves 1866 Jan 22 2003 archl

- Y ahora amigo, ve practicando esos ejemplos, porque, cuando nos encontremos nuevamente,te voy a explicar una serie de instrucciones típicas de programación Shell. Chau! Ahh! Solamenteuna cosita mas que me estaba olvidando de decirte. En Shell, el símbolo (#) es usado cuandodeseamos hacer un comentario.

$ exit # pídele la cuenta al mozo

Cualquer duda o falta de compañia para tomar una cerveza o incluso para hablar mal de lospolíticos lo único que tienes que hacer es mandarme un e-mail para [email protected]. Voyaprovechar también para mandar mi aviso publicitario: puedes decirle a los amigos que quienquiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 01 Sep 2006

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa001 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa001 12 / 123

Page 13: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa002Controles: - Última Atualização: [14 Feb 2012 - V.11]

Conversación de Bar Parte IIMe quedo con el grep, tú con la gripe

La familia grepVamos a montar una "CDteca"

Pasando parámetrosObservaciones sobre parámetros

- Mozo! Trae un "chopps" y dos "pastel". Mi amigo hoy no va a beber porque finalmente le estásiendo presentado un verdadero sistema operativo y todavia tiene muchas cosas que aprender! - Y entonces, amigo, estas entendiendo todo lo que te expliqué hasta ahora? - Entendiendo estoy, pero no vi nada práctico en eso...

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 13 / 123

Page 14: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Calma, lo que te dije hasta ahora, sirve como base para lo que ha de venir de aquí en adelante.Vamos a usar estas herramientas que vimos para montar programas estructurados, que el Shell tepermite. Entonces verás por qué hasta en la TV hubo un programa llamado "El Shell es el Límite". - Para comenzar vamos a hablar de los comandos de la familia grep. - grep? No conozco ningún término en inglés con este nombre... - Por supuesto, grep es un acrónimo Global Regular Expression Print, que usa expresionesregulares para buscar la ocurrencia de cadenas de caracteres en la entrada definida (has desaber que hay una leyenda sobre como fue bautizado este comando: en el editor de textos "ed", elabuelo del "vim", el comando usado para buscar era g/_expresión regular_/p, en inglés g/_re_/p).Por hablar en expresiones regulares (o regexp), el portal BULMA - Bisoños Usuarios deGNU/Linux de Mallorca y Alrededores tiene todos los enlaces en su página (inclusive tutoriales)que abordan el tema. Si realmente tienes ganas de aprender a programar en Shell, Perl, Python,... debes leer estos artículos que te ayudarán a resolver lo que nos espera más adelante.

Me quedo con el grep, tú con la gripeEso de la gripe es un sólo un decir! Simplemente un pretexto para pedir unas "Caipirinhas" (ocaipiriñas, el coctel oficial del carnaval brasileño - vea cómo prepararlas). Volviendo a nuestrotema, te dije que el grep busca cadenas de caracteres dentro de una entrada definida, pero enrealidad... que viene a ser una "entrada definida"? Bueno, existen varias formas de definir laentrada del comando grep. Veamos: Buscando en un archivo:

$ grep rafael /etc/passwd

buscando en varios archivos:

$ grep grep *.sh

Buscando en la salida de un comando:

$ who | grep Pelegrino

En el 1º ejemplo, el más simple, busqué la palabra rafael en cualquier lugar del archivo/etc/passwd. Si quisiera buscarla como un login name, o sea, solamente en el principio de losregistros de este archivo, debería hacer:

$ grep '^rafael' /etc/passwd

Y para que sirve este acento circunflejo y los apostrofes? me vas a preguntar. Si hubieras leído losartículos anteriores sobre expresiones regulares que te dije, sabrías que el circunflejo (^) sirve paralimitar la búsqueda al inicio de cada línea, y los apostrofes (') sirven para que el Shell no interpreteeste circunflejo, dejándolo pasar intacto para el comando grep.

Mira que bien! El grep acepta como entrada, la salida de otro comando redireccionado por unpipe (esto es muy común en Shell y es un tremendo acelerador de la ejecución de comandos, yaque actúa como si la salida de un programa fuera guardada en disco y el segundo programaleyera este archivo generado), de esta forma, en el 3º ejemplo, el comando who listó las personas"logadas" en la misma máquina que tú (no te olvides jamás: el Linux es multiusuario) y el grep fueusado para verificar si Pelegrino estaba trabajando o simplemente "haciendo sebo".

La familia grep

Este comando grep es muy conocido, pues es usado con mucha frecuencia. Algo que muchaspersonas desconocen es que existen tres comandos en la familia grep, que son:

grep

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 14 / 123

Page 15: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

egrep

fgrep

Las principales diferencias entre los 3 son:

El grep puede o no, usar expresiones regulares simples, sin embargo en caso de no usarlas,el fgrep es mejor, por ser más rápido;El egrep ("e" de extended, extendido) es muy poderoso en el uso de expresiones regulares.Por ser el más lento de la familia, solo debe ser usado cuando sea necesario la construcciónde una expresión regular que no sea aceptada por el grep;El fgrep ("f" de fast, rápido, o de "file", archivo) como el nombre dice, es el rapidito de lafamilia, ejecuta el servicio de forma muy veloz (a veces es cerca de 30% más rápido que elgrep y 50% más que el egrep), sin embargo no permite el uso de expresiones regulares en labúsqueda.

Todo lo que dije arriba sobre la velocidad, solamente se aplica a la familia de comandos grep delUnix. En Linux el grep es siempre más veloz, ya que los otros dos (fgrep y egrep) son scripts enShell que llaman al primero y, ya me estoy adelantando, no me gusta nada esta solución. - Ahora que ya conoces las diferencias entre los miembros de la familia, dime: que te parecenlos tres ejemplos que te dí antes de las explicaciones? - Me pareció que el fgrep resolvería tu problema de forma más veloz que el grep. - Perfecto! Estoy notando que estás bien atento! Estás entendiendo lo que te estoy explicando!Entonces vamos a ver más ejemplos, para aclarar de una vez por todas las diferencias del uso delos miembros de esta familia.

Ejemplos

Yo sé que en un archivo existe un texto hablando sobre Linux solo que no sé si está escrito con Lmayúscula o l minúscula. Puedo hacer la búsqueda de dos formas:

$ egrep (Linux | linux) archivo.txt

o

$ grep [Ll]inux archivo.txt

En el primer caso, la expresión regular compleja "(Linux | linux)" usa los paréntesis paraagrupar las opciones y la barra vertical (|) como un "o" lógico, o sea, estoy buscando Linux olinux.

En el segundo, la expresión regular [Ll]inux significa: comenzando por L o l seguido de inux.Como esta expresión es más simple, el grep consigue resolverla, por lo tanto creo que es mejorusar la segunda forma, ya que el egrep haría la búsqueda más lenta.

Otro ejemplo. Para listar todos los subdirectorios del directorio actual, basta hacer:

$ ls -l | grep '^d' drwxr-xr-x 3 root root 4096 Dec 18 2000 doc drwxr-xr-x 11 root root 4096 Jul 1318:58 freeciv drwxr-xr-x 3 root root 4096 Oct 17 2000 gimp drwxr-xr-x 3 root root 4096 Aug 8 2000gnome drwxr-xr-x 2 root root 4096 Aug 8 2000 idl drwxrwxr-x 14 root root 4096 Jul 13 18:58 localedrwxrwxr-x 12 root root 4096 Jan 14 2000 lyx drwxrwxr-x 3 root root 4096 Jan 17 2000 pixmapsdrwxr-xr-x 3 root root 4096 Jul 2 20:30 scribus drwxrwxr-x 3 root root 4096 Jan 17 2000 soundsdrwxr-xr-x 3 root root 4096 Dec 18 2000 xine

En el ejemplo que acabamos de ver, el circunflejo (^) sirvió para limitar la busqueda a la primeraposición de la salida del ls detallado. los apostrofes fueron colocados para que el Shell no "viera"el circunflejo (^).

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 15 / 123

Page 16: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Vamos a ver otro. Sabemos que las cuatro primeras posiciones posibles de un ls -l de unarchivo común (archivo común!, No directorio, ni link, ni ...) deben ser:

Posición 1ª 2ª 3ª 4ª

-

Valores Posibles - r w x

- - s (suid)

Así, para descubrir todos los archivos ejecutables en un determinado directorio debería hacer:

$ ls -la | egrep '^-..(x|s)' -rwxr-xr-x 1 root root 2875 Jun 18 19:38 rc -rwxr-xr-x 1 root root 857 Aug 922:03 rc.local -rwxr-xr-x 1 root root 18453 Jul 6 17:28 rc.sysinit

Donde nuevamente usamos el circunflejo (^) para limitar la búsqueda al inicio de cada línea,entonces las líneas listadas serán las que comienzan por un trazo (-), seguido de cualquier cosa (elpunto cuando es usado como una expresión regular significa cualquier cosa), nuevamente seguidode cualquier cosa, y siguiendo un x o un s.

Obtendríamos el mismo resultado si hiciéramos:

$ ls -la | grep '^-..[xs]'

y agilizaríamos la búsqueda.

Vamos a montar una "CDteca"Vamos a comenzar a desarrollar programas, me parece que montar un banco de datos demúsicas es muy útil y didáctico (y además práctico, en estos tiempos de downloads de mp3 y"quemadores" de CDs). No te olvides que, de la misma forma vamos a desarrollar una cantidad deprogramas para organizar tus CDs de música, con pequeñas adaptaciones, puedes hacer lomismo con los CDs de software que vienen con la Linux Magazine y otros que compres oquemes, si compartes este banco de software para todos los que trabajan contigo (el Linux esmultiusuario, y como tal debe ser explotado), ganarás muchos puntos con tu adorado jefe.

- Un momento! De donde voy la recibir los datos de los CDs? - Inicialmente, te voy a mostrar como tu programa puede recibir parámetros de quién lo estéejecutando y en breve, te enseñaré a leer los datos por la pantalla o de un archivo.

Pasando parámetros

El visual del archivo músicas será el siguiente:

nombre del álbum^intérprete1~nombre de la música1:..:intérprete~nombre de la música

o sea, el nombre del álbum será separado por un circunflejo (^) del resto del registro, que estáformado por diversos grupos compuestos por el intérprete de cada música del CD y la respectivamúsica interpretada. Estos grupos son separados entre sí por dos-puntos (:) e internamente, elintérprete será separado por una tilde (~) del nombre de la música.

Escribiré un programa llamado musinc, que incluirá registros en mi archivo músicas. Pasaré elcontenido de cada álbum como parâmetro en la llamada del programa de la siguiente forma:

$ musinc "álbum^interprete~música:interprete~música:..."

De esta forma el programa musinc estará recibiendo los datos de cada álbum como si fuera una

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 16 / 123

Page 17: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

variable. La única diferencia entre un parámetro recibido y una variable es que los primerosreciben nombres numéricos (nombre numérico suena algo raro, no?). Lo que quise decir es quesus nombres son formados por un y solamente un algarismo), el sea $1, $2, $3, ..., $9.Vamos, antes de todo, hacer un test:

Ejemplos

$ cat test #!/bin/bash # Programa para verificar el pasaje de parámetros echo "1o. param -> $1"echo "2o. param -> $2" echo "3o. param -> $3"

Vamos a ejecutarlo:

$ test pasando parámetros para verificar bash: test: cannot execute

Opa! Me olvidé de hacerlo ejecutable. Voy a hacerlo de forma que permita que todos puedanejecutarlo y en seguida voy a testearlo:

$ chmod 755 test $ test pasando parámetros para verificar 1o. param -> pasando 2o. param ->parámetros 3o. param -> para

Repara que la palabra verificar, que sería el cuarto parámetro, no fue listada. Esto sucediójustamente porque el programa test solo listaba los tres primeros parámetros. Vamos ejecutarlo deotra forma:

$ test "pasando parámetros" para verificar 1o. param -> pasando parámetros 2o. param -> para3o. param -> verificar

Las comillas no dejaron que el Shell viese el espacio en blanco entre las palabras y las considerócomo un único parámetro.

Observaciones sobre parámetros

Ya que estamos hablando de pasar parámetros observa bien lo siguiente:

Significado de las Principales Variables Referentes a los Parámetros

$* Contiene el conjunto de todos los parámetros (muy parecido con $@)

Variable Significado $0 Contiene el nombre del programa $# Contiene la cuantidad de parámetros pasados

Ejemplos

Vamos a alterar el programa test para usar las variables que acabamos de ver. Vamos hacerloasí:

$ cat test #!/bin/bash # Programa para verifivar el paso de parámetros (2a. Versao) echo Elprograma $0 recibió $# parámetros echo "1o. param -> $1" echo "2o. param -> $2" echo "3o.param -> $3" echo Todos de una sola \"bolada\": $*

Repare que antes de las comillas usé una barra invertida para esconderlas de la interpretación delShell (si no usase las contrabarras las comillas no aparecerian). Vamos a ejecutarlo:

$ test pasando parámetros para verificar El programa test recibió 4 parámetros 1o. param ->pasando 2o. param -> parámetros 3o. param -> para Todos de una sola "bolada": pasandoparámetros para verificar

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 17 / 123

Page 18: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Como ya dije, los parámetros reciben números de 1 a 9, pero eso no significa que no puedo usarmás de 9 parámetros, significa solamente que solo puedo direccionar 9. Vamos a verificar eso:

Ejemplo:

$ cat test #!/bin/bash # Programa para verificar el pasaje de parámetros (3a. Versión) echo Elprograma $0 recebió $# parámetros echo "11o. param -> $11" shift echo "2o. param -> $1" shift 2echo "4o. Param -> $1"

Vamos a ejecutarlo:

$ test pasando parámetros para verificar El programa test recibió 4 parámetros que son: 11o.param -> pasando1 2o. param -> parámetros 4o. param -> verificar

Dos cosas muy interesantes en este script:

1. Para mostrar que los nombres de los parámetros varían de $1 a $9 hice un echo $11 y quepasó? El Shell interpretó como que era $1 seguido del algarismo 1 y listó pasando1;

2. El comando shift cuya sintáxis es shift n, pudiendo el n asumir cualquier valor numérico(sin embargo su default es 1, como en el ejemplo dado), desprecia los n primerosparámetros, devolviendo el parámetro de orden n+1, el primero o sea, el $1.

Bueno, ahora que ya sabes más sobre pasar parámetros que yo mismo, vamos a volver a nuestra"CDteca" para hacer el script que incluira los CDs en mi banco llamado musicas. El programa esmuy simple (como todo en Shell) y voy a listarlo para que lo veas:

Ejemplos

$ cat musinc #!/bin/bash # Incluye CDs (versión 1) # echo $1 >> musicas

El script es fácil y funcional, me limito a añadir al final del archivo musicas el parámetro recibido.Vamos a incluir 3 álbumes para ver si funciona (y para no hacerlo muy aburrido, voy a suponer queen cada CD existem solamente 2 músicas):

$ musinc "album 3^Artista5~Musica5:Artista6~Musica5" $ musinc "album1^Artista1~Musica1:Artista2~Musica2" $ musinc "album 2^Artista3~Musica3:Artista4~Musica4"

Muestro ahora el contenido de musicas.

$ cat musicas album 3^Artista5~Musica5:Artista6~Musica6 album1^Artista1~Musica1:Artista2~Musica2 album 2^Artista3~Musica3:Artista4~Musica4

No es tan funcional como esperaba que quedase... podía haber quedado mejor. Los álbumesestán fuera de orden, dificultando la búsqueda. Vamos a alterar nuestro script y después aprobarlo nuevamente:

$ cat musinc #!/bin/bash # Incluye CDs (versión 2) # echo $1 >> musicas sort musicas -o musicas

Vamos a incluir un álbum más:

$ musinc "album 4^Artista7~Musica7:Artista8~Musica8"

Ahora vamos a ver lo que pasó con el archivo musicas:

$ cat musicas album 1^Artista1~Musica1:Artista2~Musica2 album2^Artista3~Musica3:Artista4~Musica4 album 3^Artista5~Musica5:Artista6~Musica5 album4^Artista7~Musica7:Artista8~Musica8

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 18 / 123

Page 19: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Simplemente incluí una línea que clasifica el archivo musicas dándole la salida sobre si mismo(para eso sirve la opción -o), después de que cada álbum fue incluído.

Opa! Ahora está quedando bien y casi funcional. Pero atención, no te desesperes! Esta no es laversión final. El programa quedará mucho mejor y más amigable, en una nueva versión queharemos después que aprendamos la adquirir los datos de la pantalla y a formatear la entrada

Ejemplos

Usar el comando cat para listar no es una buena idea, vamos a hacer un programallamado=muslist=, para listar un álbum cuyo nombre será pasado como un parámetro:

$ cat muslist #!/bin/bash # Consulta CDs (versión 1) # grep $1 musicas

Vamos a ejecutarlo, buscando el album 2. Como ya vimos anteriormente, para pasar la cadena decaracteres album 2 es necesario protegerla de la interpretación del Shell, así él no la interpretacomo dos parámetros separados. Vamos a hacerlo de la siguiente forma:

$ muslist "álbum 2" grep: can't open 2 musicas: album 1^Artista1~Musica1:Artista2~Musica2musicas: album 2^Artista3~Musica3:Artista4~Musica4 musicas: album3^Artista5~Musica5:Artista6~Musica6 musicas: album 4^Artista7~Musica7:Artista8~Musica8

Que desorden! Donde está el error?. Tuve buen cuidado de colocar el parámetro pasado entrecomillas, para que el Shell no lo diviera en dos!

Si, pero advierte ahora como el grep está siendo ejecutado:

grep $1 musicas

Aunque coloque álbum 2 entre comillas, para que fuera visto como un único parámetro, cuando el$1 fue pasado por el Shell hacia el comando grep, lo transformó en dos argumentos. Así, elcontenido final de la línea que el comando grep ejecutó fue el siguiente:

grep album 2 musicas

Como la sintáxis del grep es:

grep <cadena de caracteres> [arch1, arch2, ..., archn]

el grep entendió que debería recuperar la cadena de caracteres album en los archivos 2 ymusicas, Al no existir el archivo 2 generó el error, y como encontro la palabra album en todos losregistros de musicas, los listo todos.

Siempre que la cadena de caracteres a ser pasada hacia el comando grep posea blancos o TAB,y lo mismo que dentro de variables, colóquela siempre entre comillas para evitar que las palabrasdespués del primer espacio en blanco o TAB sean interpretadas como nombres de archivos.

Por otro lado, es mejor ignorar las mayúsculas y minúsculas en la búsqueda. Resolveríamos losdos problemas si el programa tuviera la siguiente forma:

$ cat muslist #!/bin/bash # Consulta CDs (versión 2) # grep -i "$1" musicas

En este caso, usamos la opción -i del grep, que como vimos, sirve para ignorar mayúsculas yminúsculas, y colocamos el $1 entre comillas, para que el grep continue viendo la cadena decaracteres resultante de la expansión de la línea por el Shell como un único argumento debúsqueda.

$ muslist "album 2" album2^Artista3~Musica3:Artista4~Musica4

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 19 / 123

Page 20: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Ahora, nota que el grep localiza la cadena buscada en cualquier lugar del registro, entonces de laforma que estamos lo haciendo, podemos buscar por álbum, por música, por intérprete o hasta porun pedazo de cualquiera de estos. Cuando conozcamos los comandos condicionales,montaremos una nueva versión de muslist que permitirá especificar por que campo buscar.

Ahora me vas a decir:

- Si, todo bien, pero es muy tedioso tener que colocar el argumento de búsqueda entre comillascuando tengo que pasar el nombre del álbum. Esta forma no es nada amigable! - Tienes toda la razón, y es por eso que te voy a mostrar otra forma de hacer lo que me pediste:

$ cat muslist #!/bin/bash # Consulta CDs (versión 3) # grep -i "$*" musicas $ muslist album 2 album2^Artista3~Musica3:Artista4~Musica4

De esta forma, el $*, que significa todos los parámetros, será substituído por la cadena album 2(de acuerdo con el ejemplo anterior), haciendo lo que tu querias.

No te olvides que el problema del Shell no es si él puede o no hacer una determinada cosa. Elproblema es decidir cuál es la mejor forma de hacerla, ya que para realizar cualquier tarea, lacantidad de opciones es enorme.

Recuerdas aquel dia de verano fuiste a la playa?, olvidaste el CD en el automóbil, y entonces aquel"solecito" de 40 grados dobló tu CD y ahora necesitas una herramienta para borrarlo del banco dedatos. No hay ningún problema, vamos a desarrollar un script llamado musexc, para excluir estosCDs.

Antes de desarrollar el programa te quiero presentar una opción bastante útil de la familia decomandos grep. Es la opción -v, que cuando es usada, lista todos los registros de la entrada,excepto el(los) localizado(s) por el comando. Veamos:

Ejemplos

$ grep -v "album 2" musicas album 1^Artista1~Musica1:Artista2~Musica2 album3^Artista5~Musica5:Artista6~Musica6 album 4^Artista7~Musica7:Artista8~Musica8

De acuerdo con lo que te expliqué antes, el grep del exemplo listó todos los registros de músicasexcepto los referentes al album 2, porque atendía al argumento del comando. Estamos entoncespreparados para desarrollar un script para retirar aquél CD doblado de tu "CDteca". Este scripttiene la forma siguiente:

$ cat musexc #!/bin/bash # Borra CDs (versión 1) # grep -v "$1" musicas > /tmp/mus$$ mv -f/tmp/mus$$ musicas

En la primera línea mandé hacia /tmp/mus$$ el archivo musicas, sin los registros que tuviesen laconsulta hecha por el comando grep. En seguida, moví (que, en realidad, equivale a renombrarlo)/tmp/mus$$ al antiguo musicas.

Usé el archivo /tmp/mus$$ como archivo de trabajo, porque como ya habia citado en el artículoanterior, el $$ contiene el PID (Process Identification o identificación del proceso) y de esta formacada uno que edite el archivo musicas lo hará en un archivo de trabajo diferente, de esta formaevitamos choques en su uso.

- Y entonces, amigo, estos programas que hicimos hasta aquí son muy rústicos en virtud de lafalta de herramientas que todavia tenemos. Pero están bien, mientras me tomo otro "chopp",puedes ir para casa a praticar en los ejemplos dados porque, te prometo, llegaremos a desarrollarun sistema bien bonito para el control de tus CDs.

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 20 / 123

Page 21: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Cuando nos encontremos la próxima vez, te voy a enseñar como funcionan los comandoscondicionales y mejoraremos otro poco estos scripts. - Por hoy es suficiente! Ya hablé demasiado y necesito mojar las palabras porque estoy con lagarganta seca! - Mozo! Otro sin y espuma!

Cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar mal de lospolíticos, lo único que tienes que hacer es mandarme un e-mail para [email protected]. Voyaprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos que quienquiera hacer un curso nota diez de programación en Shell, que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 13 Sep 2006

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa002 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa002 21 / 123

Page 22: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa003Controles: - Última Atualização: [14 Feb 2012 - V.8]

Conversación de Bar Parte IIITrabajando con cadenas

El Comando cut (que no es la central única de trabajadores)El comando cut con la opción -cEl comando cut con la opción -f

Si hay cut hay pasteQuién está de pié, se acuesta.Usando separadores

El Comando trCambiando caracteres con trSacando caracteres con tr

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 22 / 123

Page 23: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

X(com)primiendo con trComandos Condicionales

El Comando if

- Mozo, traiga dos "choppes" por favor, que hoy voy a tener que hablar mucho.

Trabajando con cadenasPor el título de arriba no pienses que te voy a enseñar a ser carcelero! Me estoy refiriendo acadenas de caracteres!

El Comando cut (que no es la central única de trabajadores)

Primero te quiero mostrar, de forma eminentemente práctica, una instrucción simple de usar y muyútil: el comando cut, Esta instrucción es usada para cortar un determinado pedazo de un archivo ytiene dos formas distintas de uso:

El comando cut con la opción -c

Con esta opción, el comando tiene la siguiente sintáxis:

cut -c PosIni-PosFin [archivo]

Donde:

PosIni = Posición inicial PosFin = Posición final

$ cat números 1234567890 0987654321 1234554321 9876556789 $ cut -c1-5 números 1234509876 12345 98765 $ cut -c-6 números 123456 098765 123455 987655 $ cut -c4- números4567890 7654321 4554321 6556789 $ cut -c1,3,5,7,9 números 13579 08642 13542 97568 $ cut-c-3,5,8- números 1235890 0986321 1235321 9875789

Como se puede ver, en realidad existen cuatro sintaxis distintas: en la primera (-c1-5),especifiqué una franja de posiciones, en la segunda (-c-6), especifiqué todas las posicioneshasta una determinada columna, en la tercera (-c4-) de una determinada posición en adelante yen la cuarta (-c1,3,5,7,9), determinadas posiciones. La última (-c-3,5,8-) fue solo parademostrar que lo podemos mezclar todo.

El comando cut con la opción -f

Pero no pienses que acabó por ahí! Como debes haber notado, esta forma de cut es útil paraarchivos con campos de tamaño fijo, sin embargo, actualmente lo que más existe son archivos concampos de tamaño variables, donde cada campo termina con un delimitador. Vamos a dar unaojeada al archivo musicas que comenzamos a preparar en nuestra conversación de la última vezque estuvimos aquí, en el bar.

$ cat musicas album 1^Artista1~Musica1:Artista2~Musica2 album2^Artista3~Musica3:Artista4~Musica4 album 3^Artista5~Musica5:Artista6~Musica5 album4^Artista7~Musica7:Artista8~Musica8

Entonces, recapitulando, su "layout" es el siguiente:

nombre del album^intérprete1~nombre de la música1:...:intérpreten~nombre de lamúsican

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 23 / 123

Page 24: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

O sea, el nombre del álbum será separado por un circunflejo (^) del resto del registro, que estáformado por diversos grupos, compuestos por el intérprete de cada música del CD y la respectivamúsica interpretada. Estos grupos son separados entre sí por dos puntos (:) e internamente, elnombre del intérprete será separado por una tilde (~), del nombre de la música.

Entonces para sacar los datos referentes a todas las segundas músicas del archivo musicas,debemos hacer:

$ cut -f2 -d: musicas Artista2~Musica2 Artista4~Musica4 Artista6~Musica5 Artista8~Musica8

O sea, cortamos el segundo campo (-f de field en inglés), delimitado (-d) por dos puntos (:). Encambio, se quisiéramos solamente los intérpretes, podriamos hacer:

$ cut -f2 -d: musicas | cut -f1 -d~ Artista2 Artista4 Artista6 Artista8

Para entender esto, vamos a sacar la primera línea de musicas:

$ head -1 musicas album 1^Artista1~Musica1:Artista2~Musica2

Ahora observa lo que ocurrió:

Delimitador del primer cut (:)

album 1^Artista1~Musica1:Artista2~Musica2

De esta forma, en el primer cut, el primer campo del delimitador (-d) dos puntos (:), es album1^Artista1~Musica1 y el segundo, que es lo que nos interesa, es Artista2~Musica2.

Vamos ahora a ver lo que pasó con el segundo cut:

Nuevo delimitador (~)

Artista2~Musica2

Ahora, el primer campo del delimitador (-d) tilde (~), que es el que nos interesa, es Artista2 y elsegundo es Musica2.

Si el razonamiento que hicimos para la primera línea fuera aplicado al resto del archivo,llegaríamos a la respuesta anteriormente dada.

Si hay cut hay paste

Como ya era de esperar, el comando paste sirve para pegar, sólo que aquí en Shell lo que pegason archivos. Para empezar a entenderlo, vamos a hacer esto::

paste arch1 arch2

De esta forma el comando mandará hacia la salida patrón (stdout) cada uno de los registros dearch1, al lado de los registros de arch2 correspondientes y en caso de que no se especifiqueningún delimitador, usará por default el <TAB>.

El paste es un comando poco usado por que su sintaxis es poco conocida. Vamos a jugar con 2archivos creados de la siguiente forma:

$ seq 10 > enteros $ seq 2 2 10 > pares

Para ver el contenido de los archivos creados, vamos a usar el paste en su forma simple que

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 24 / 123

Page 25: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

mostramos arriba:

$ paste enteros pares 1 2 2 4 3 6 4 8 5 10 6 7 8 9 10

Quién está de pié, se acuesta.

Ahora vamos a transformar la columna del pares en línea:

$ paste -s pares 2 4 6 8 10

Usando separadores

Como ya fue dicho, el separador default del paste es el <TAB>, pero eso puede ser alterado con laopción -d. Entonces para calcular la suma del contenido de pares primeramente haríamos:

$ paste -s -d'+' pares # también podría ser -sd'+' 2+4+6+8+10

y después pasaríamos esta línea con pipe (|) hacia la calculadora (bc), y entonces quedaría:

$ paste -sd'+' pares | bc 30

De esta forma, para calcular el factorial del número contenido en $Num, basta hacer:

$ seq $Num | paste -sd'*' | bc

Con el comando paste tu también puedes montar formatos exóticos como el siguiente:

$ ls | paste -s -d'\t\t\n' arch1 arch2 arch3 arch4 arch5 arch6

Lo que pasó fue lo siguiente: se le especifico al comando paste que tendría que transformar líneasen columnas (por la opción -s) y que sus separadores (si...! acepta más de uno, pero solamenteuno después de cada columna creada por el comando) serían un <TAB>, otro <TAB> y un <ENTER>,generando de esta forma su salida tabulada en 3 columnas.

Ahora que ya entendiste esto, observa como hacer lo mismo, pero de forma más fácil, menosextraño y primitivo, usaremos el mismo comando pero con la siguiente sintaxis:

$ ls | paste - - - arch1 arch2 arch3 arch4 arch5 arch6

Y esto sucede porque si en lugar de especificar los archivos, colocamos el signo de menos (-), elcomando paste los substituye por la salida o entrada patrón conforme al caso. En el ejemploanterior los datos fueran mandados hacia la salida patrón (stdout), porque el pipe (|) estabadesviando la salida del ls hacia la entrada patrón (stdin) del paste, pero veamos el ejemplosiguiente:

$ cat arch1 predisposición privilegiado profesional $ cat arch2 ver mario motor $ cut -c-3 arq1 |paste -d "" - arq2 prever primario promotor

En este caso, el cut devolvió las tres primeras letras de cada registro de arch1, el paste fuemontado para no tener separador (-d"") y recibir la entrada patrón (desviada por el pipe) en eltrazo (-), generando la salida junto con arch2.

El Comando tr

Otro comando muy interesante es el tr que sirve para substituir, comprimir o retirar caracteres. Susintaxis sigue el siguiente patrón:

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 25 / 123

Page 26: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

tr [opciones] cadena1 [cadena2]

El comando tr copia el texto de la entrada patrón (stdin) y cambia, las veces que encuentre, loscaracteres de cadena1 por el correspondiente contenido de la cadena2, o cambia las múltiplescoincidencias de los caracteres de cadena1 por solamente un carácter, o todavía puede hacermas, puede eliminar los caracteres de la cadena1.

Las principales opciones del comando son:

Principales Opciones del comando tr

-d Elimina del archivo los caracteres de la cadena1

Opción Significado -s Comprime n coincidencias de la cadena1 en sólo una

Cambiando caracteres con tr

Primero te voy a dar un ejemplo bien bobo:

$ echo bobo | tr o a baba

O sea, cambié todas las coincidencias de la letra o por la letra a.

Suponte que en un determinado punto de mi script, pido al operador que teclee s o n (si o no), yguardo su respuesta en la variable $Resp. El contenido de $Resp puede estar con letrasmayúsculas o minúsculas, y de esta forma tendría que hacer diversos tests para saber si larespuesta dada fue S, s, N o n. Entonces lo mejor es hacer:

$ Resp=$(echo $Resp | tr SN sn)

y despues de ejecutar este comando tendría la seguridad de que el contenido de $Resp seria un so un n.

Si mi archivo ArchEnt está todo escrito con letras mayúsculas y deseo pasarlas para minúsculashago:

$ tr A-Z a-z < ArchEnt > /tmp/$$ $ mv -f /tmp/$$ ArchEnt

Observa que en este caso usé la notación A-Z para no tener que escribir ABCD...YZ. Otro tipo denotación que puede ser usada son las escape sequences (preferiría escribir en español, pero eneste caso como lo traduciría? Secuencias de escape? Medio sin sentido, no te parece? Perocontinuemos...) que también son reconocidas por otros comandos y también en lenguaje C, y cuyosignificado verás a continuación:

Escape Sequences

\\ Una barra invertida \0134

Secuencia Significado Octal \t Tabulación \011\n Nueva línea \012\v Tabulación Vertical \013\f Nueva Página \014\r Início de línea <^M> \015

Sacando caracteres con tr

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 26 / 123

Page 27: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Dejame contarte un "causo": un alumno que estaba enojado conmigo, decidió complicarme la viday en un ejercicio práctico que pasé para ser hecho en el computador, y que valía para nota, meentregó el script con todos los comandos separados por punto y coma (recuerdas que te dije queel punto y coma servía para separar diversos comandos en una misma línea?).

Te voy a dar un ejemplo simplificado e idiota de un "chorizo" así:

$ cat confuso echo lea Programación Shell Linux de Julio Cezar Neves > libro;cat libro;pwd;ls;rm -flixo 2>/dev/null;cd ~

Yo ejecutaba el programa y se ejecutaba así:

$ confuso lea Programación Shell Linux de Julio Cezar Neves /home/jneves/LM confuso livromusexc musicas musinc muslist numeros

Pero nota de prueba es cosa seria (y billete de dólar todavia más ) entonces, para entender lo queel alumno habia hecho, lo llamé y delante suyo ejecuté el siguiente comando:

$ tr ";" "\n" < confuso echo lea Programación Shell Linux de Julio Cezar Neves pwd ls rm -f lixo2>/dev/null cd ~

El alumno se quedó muy triste, porque en 2 o 3 segundos le deshice la broma en la había perdidovarias horas.

Ahora fíjate bien! Si yo tuviera una máquina con Unix, habria hecho lo siguiente:

$ tr ";" "\012" < confuso

X(com)primiendo con tr

Observa ahora la diferencia entre los dos comandos date: el que hice hoy y el otro que fueejecutado hace dos semanas:

$ date # Hoy Sun Sep 19 14:59:54 2006 $ date # Hace dos semanas Sun Sep 5 10:12:33 2006

Para ver la hora debería hacer:

$ date | cut -f 4 -d ' ' 14:59:54

Sin embargo, dos semanas antes ocurriría lo siguiente:

$ date | cut -f 4 -d ' ' 5

Ahora observa porqué:

$ date # Hace dos semanas Sun Sep 5 10:12:33 2004

Como puedes notar, existen 2 caracteres en blanco antes del 5 (día), esto lo confunde todo porqueel tercer pedazo está vacio y el cuarto es el día (5). Entonces lo ideal sería comprimir los espaciosen blanco sucesivos en solamente un espacio, para poder tratar las dos cadenas resultantes delcomando date de la misma forma, y eso se hace así:

$ date | tr -s " "a Sun Sep 5 10:12:33 2004

Como puedes ver, no existen los dos espacios, Entonces ahora podria cortar:

$ date | tr -s " " | cut -f 4 -d " " 10:12:33

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 27 / 123

Page 28: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Viste como el Shell ya está solucionando problemas! Observa este archivo que fue bajado de unamáquina con aquél sistema operativo que sufre de todos los vírus:

$ cat -ve ArqDoDOS.txt Este archivo^M$ fue generado por^M$ DOS/Rwin y fue^M$ bajado porun^M$ ftp mal hecho.^M$

y ahora te quiero dar dos consejos:

Consejo #1 - La opción -v del cat muestra los caracteres de control invisibles, con la notación ^L,donde ^ es la tecla control y L es la respectiva letra. La opción -e muestra el final de la línea comoun signo de pesos ($).

Consejo #2 - Esto ocurre porque en formato DOS (o rwin), el fin de los registros está formado porun carriage-return (\r) y un line-feed (\n). En Linux sin embargo, el final del registro tienesolamente el line-feed.

Vamos entonces a limpiar este archivo.

$ tr -d '\r' < ArchDeDOS.txt > /tmp/$$ $ mv -f /tmp/$$ ArchDeDOS.txt

Ahora vamos a ver lo que pasó:

$ cat -ve ArchDeDOS.txt Este archivo$ fue generado por el$ DOS/Rwin y fue$ bajado por un$ ftpmal hecho.$

Bien, la opción -d del tr retira los caracteres especificados de todo el archivo. De esta formaretiré los caracteres no deseados, grabandolo en un archivo de trabajo temporal y posteriormentelo renombré con su nombre original.

Obs: En Unix debería hacer:

$ tr -d '\015' < ArchDeDOS.txt > /tmp/$$

Esto pasó porque el ftp fue hecho de modo binario (o image), o sea, sin la interpretación del texto.Si antes de la transmisión del archivo hubiera sido estipulada la opción ascii del ftp, esto nohabría ocurrido.

- Mira, después de este consejo, estoy comenzando a disfrutar de ese tal Shell, pero todaviahay muchas cosas que no consigo hacer.

- Claro!, si hasta aqui no te hablé casi nada sobre programación en Shell, tenemos muchascosas aun por avanzar, sin embargo, con lo que aprendiste, ya te da para resolver muchosproblemas, hasta que tú adquieras el “modo Shell de pensar”. Serías capaz de hacer un scriptpara decirme quienes son las personas que están “logadas” desde hace más de un dia en tuservidor?

- Claro que no! Para eso seria necesario que conociera los comandos condicionales quetodavia no me explicaste como funcionan.

- Dejame intentar cambiar un poco tu lógica y atraerla hacia el “modo Shell de pensar”, peroantes es mejor tomar un chope... Chico!, traeme otros dos...

- Ahora que ya moje el gaznate, vamos a resolver el problema que te propuse. Presta atención acomo funciona el comando who:

$ who jneves pts/1 Sep 18 13:40 rtorres pts/0 Sep 20 07:01 rlegaria pts/1 Sep 20 08:19 lcarlospts/3 Sep 20 10:01

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 28 / 123

Page 29: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Y mira también el date:

$ date Mon Sep 20 10:47:19 BRT 2004

Ves que el mes y el dia están en el mismo formato en ambos comandos?

Algunas vezes un comando tiene la salida en portugués y el otro en inglés. Cuando eso pase,puedes usar el siguiente artificio:$ date Mon Sep 20 10:47:19 BRT 2004 $ LANG=pt_BR date Seg Set 20 10:47:19 BRT 2004Y así pasas la salida del comando date hacia portugués, o hacia cualquier otro idioma quequieras.

Entonces, si en algún registro del who no encuentro la fecha de hoy, significa que el individuo está"logado" hace más de un día, ya que él no puede haberse "logado" mañana... Vamos a guardar elpedazo que importa de la fecha de hoy para buscarla en la salida del who:

$ Fecha=$(date | cut -c 5-10)

Aquí usé la construcción $(...), para dar prioridad a la ejecución de los comandos antes deatribuir a su salida a la variable $Fecha. Vamos a ver si funcionó:

$ echo $Fecha Sep 20

Muy bien! Ahora, lo que tenemos que hacer es buscar con el comando who los registros que noposeen esta fecha.

- Ah! Me parece que estoy entendiendo! Ahora que mencionaste lo de buscar, se me ocurrió elcomando grep, acerté?

- Correctísimo! Solo que tengo que usar el grep con aquella opción que solamente lista losregistros en los quales no encontró la cadena. Te acuerdas que opción es esa?

- Claro, es la opción -v...

- Eso mismo! Estás quedando un lujo! Entonces vamos a ver:

$ who | grep -v "$Fecha" jneves pts/1 Sep 18 13:40

- Y si quisiera un poco mas de adornos,haría así:

$ who | grep -v "$Fecha" | cut -f1 -d ' ' jneves

- Te diste cuenta? No fue necesario usar ningún comando condicional, porque además nuestrocomando condicional más usado, el famoso if, no verifica condición sino instrucciones, comoveremos ahora.

Comandos CondicionalesObserva las líneas de comando que siguen:

$ ls musicas musicas $ echo $? 0 $ ls ArchInexistente ls: ArchInexistente: No such file or directory $echo $? 1 $ who | grep jneves jneves pts/1 Sep 18 13:40 (10.2.4.144) $ echo $? 0 $ who | grepjuliana $ echo $? 1

- y que hace ese $? por ahí? Comenzando por pesos ($) parece ser una variable, correcto?

- Si, es una variable que contiene el código de salida de la última instrucción ejecutada. Te

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 29 / 123

Page 30: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

puedo garantizar que si esta instrucción fué bien ejecutada, $? tendrá el valor cero, en casocontrario su valor será diferente de cero.

El Comando if

Lo que nuestro comando condicional if hace es testar la variable $?. Veamos entonces a ver susintaxis:

if cmd then cmd1 cmd2 cmdn else cmd3 cmd4 cmdm fi

o sea: en caso que el comando cmd haya sido ejecutado con éxito, los comandos del bloque delthen (cmd1, cmd2 y cmdn) serán ejecutados, en caso contrario, los comandos ejecutados serán losdel bloque opcional del else (cmd3, cmd4 y cmdm), terminando con un fi.

Vamos a ver en la prática como funciona eso usando un scriptisiño que sirve para incluir usuáriosen el /etc/passwd:

$ cat incusu #!/bin/bash # Versión 1 if grep ^$1 /etc/passwd then echo Usuario \'$1\' ya existe elseif useradd $1 then echo Usuario \'$1\' incluído en /etc/passwd else echo "Problemas en el catastro.Usted es root?" fi fi

Nota que el if está verificando diretamente el comando grep y ésta es su finalidad. En caso deque el if sea exitoso, o sea, el usuário (cuyo nombre está en $1) fuera encontrado en /etc/passwd,los comandos del bloque del then serán ejecutados (en este ejemplo es solamente el echo) y en elcaso contrario, las instrucciones del bloque del else son las que serán ejecutadas, entonces unnuevo if verifica si el comando useradd fué bien ejecutado , creando el registro del usuario en/etc/passwd, o no, y es entonces cuando dará el mensaje de error.Veamos su ejecución, primero pasando un usuario ya existente:

$ incusu jneves jneves:x:54002:1001:Julio Neves:/home/jneves:/bin/bash Usuario 'jneves' ya existe

Como ya vimos diversas veces, pero siempre es bueno insistir en el tema para que te quede claro,en el ejemplo anterior surgió una línea no deseada, esta es la salida del comando grep. Para evitarque eso pase, debemos desviar la salida de esta instrucción para /dev/null, quedando así:

$ cat incusu #!/bin/bash # Versión 2 if grep ^$1 /etc/passwd > /dev/null # o: if grep -q ^$1/etc/passwd then echo Usuario \'$1\' ya existe else if useradd $1 then echo Usuario \'$1\' incluído en/etc/passwd else echo "Problemas en el catastro. Usted es root?" fi fi

Ahora vamos a verificarlo, pero como usuario normal (no root):

$ incusu JuanNadie ./incusu[6]: useradd: not found Problemas en el catastro. Usted es root?

Epa!, aquél error no tenia que pasar! Para evitar que eso suceda debemos mandar también lasalida de error (strerr, te acuerdas?) del useradd hacia /dev/null, quedando la versión final así:

$ cat incusu #!/bin/bash # Versión 3 if grep ^$1 /etc/passwd > /dev/null then echo Usuario \'$1\' yaexiste else if useradd $1 2> /dev/null then echo Usuario \'$1\' incluído en /etc/passwd else echo"Problemas en el catastro. Usted es root??" fi fi

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 30 / 123

Page 31: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Después de estas alteraciones y de hacer un su – (volverme root) veamos su comportamiento:

$ incusu perez Usuario 'perez' incluído en /etc/passwd

Y nuevamente:

$ incusu perez Usuario 'perez' ya existe

Recuerdas que te dije que a lo largo de nuestras conversaciones y "choppes" nuestros programasirían mejorando? Entonces veamos ahora como podríamos mejorar nuestro programa para incluirmúsicas:

$ cat musinc #!/bin/bash # Incluye CDs (versión 3) # if grep "^$1$" musicas > /dev/null then echoEste álbum ya está incluído else echo $1 >> musicas sort musicas -o musicas fi

Como viste, es una pequeña evolución de la versión anterior, de forma que, antes de incluir unregistro (que con la versión anterior podría ser duplicado), verificamos si el registro comenzaba (^)y terminaba ($) igual al parámetro pasado ($1). El uso del circunflejo (^) en el inicio de la cadena yel pesos ($) en el fin, son para verificar si el parámetro pasado (el álbum y sus datos) sonexactamente iguales a algún registro anteriormente incluído y no unicamente igual a un pedazo dealguno de los registros.

Vamos a ejecutarlo pasando un álbum ya anteriormente incluído:

$ musinc "album 4^Artista7~Musica7:Artista8~Musica8" Este álbum ya está incluído

Y ahora uno no incluído:

$ musinc "album 5^Artista9~Musica9:Artista10~Musica10" $ cat musicas album1^Artista1~Musica1:Artista2~Musica2 album 2^Artista3~Musica3:Artista4~Musica4 album3^Artista5~Musica5:Artista6~Musica5 album 4^Artista7~Musica7:Artista8~Musica8 album5^Artista9~Musica9:Artista10~Musica10

- Como viste, el programa mejoró un poquito, pero todavia no está listo. A medida que te vayaenseñando a programar en shell, nuestra CDteca va a ir quedando cada vez mejor.

- Entendí todo lo que me explicaste, pero todavia no sé como hacer un if para verificarcondiciones, o sea el uso normal del comando.

- Mira, para eso existe el comando test, él es quien verifica condiciones. El comando ifverifica el comando test. Pero eso está medio confuso y como ya hablé mucho, estoy necesitandounos "choppes" para mojar las palabras. Vamos a parar por aqui y la próxima vez te explicoclaramente el uso del test y de diversas otras sintáxis del if.

- Estamos de acuerdo entonces! Me parece bien porque yo tambiém estoy quedando tonto yasí aprovecho para practicar esa cantidad de cosas de las cuales me hablaste hoy .

- Para memorizar lo que aprendiste, trata de hacer un scriptiziño para informar si undeterminado usuario, que será pasado como parámetro está logado (ajjjhh!) o no.

- Chico,dos "choppes" más por favor...

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos quequien quiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 31 / 123

Page 32: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Gracias y hasta la próxima

-- HumbertoPina - 04 Oct 2006

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa003 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa003 32 / 123

Page 33: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa004Controles: - Última Atualização: [04 Feb 2008 - V.12]

Conversación de bar IV

Conversación de bar IVEl Comando testQuerida, Encojieron el Comando Condicional!Y toma test!Acaso Casa con case

- Y entonces amigo mio, intentaste hacer el ejercicio que te pedí para reforzar tusconocimientos?

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 33 / 123

Page 34: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Claro, que si! En programación, si no se practica, no se aprende. Me pediste que hiciera unscriptisiño para informar si un determinado usuario, que será pasado como parámetro, estalogado (ajjjj!) o no. Hice lo siguiente:

$ cat logado #!/bin/bash # Busca si una persona está logada o no if who | grep $1 then echo $1está logado else echo $1 no se encuentra en la vecindad fi

- Calma amigo! Ya vi que hoy llegaste lleno de deseos de trabajar, primero vamos a pedirnuestros "choppes" de costumbre y después vamos al Shell. Chico!, tráeme dos "choppes", unosin espuma!

- Ahora que ya mojamos nuestros labios, vamos a echar un vistazo a la ejecución de tuprograma:

$ logado jneves jneves pts/0 Oct 18 12:02 (10.2.4.144) jneves está logado

Realmente funcionó. Pasé mi login como parámetro y él me informó que estaba logado, sinembargo, al mismo tiempo salió una línea que no pedí. Esta línea es la salida del comando who, ypara evitar que eso pase, lo único que hay que hacer es mandarla hacia el agujero negro que aestas altura ya sabes que es el /dev/null. Veamos entonces como quedaría:

$ cat logado #!/bin/bash # Busca si una persona está logada o no (versión 2) if who | grep $1 >/dev/null then echo $1 está logado else echo $1 no se encuentra en la vecindad fi

Ahora vamos a los tests:

$ logado jneves jneves está logado $ logado chico chico no se encuentra en la vecindad

Ah, ahora si! Acuérdate de esto: la mayor parte de los comandos tienen una salida patrón y unasalida de errores (el grep es una de las pocas excepciones, ya que no da mensajes de errorcuando no encuentra una cadena) y es necesario estar muy atentos para redirecionarlas hacia elagujero negro cuando sea necesario.

Bueno, ahora vamos a cambiar de asunto: la última vez que nos encontramos aquí en el Bar, teestaba mostrando los comandos condicionales y cuando ya estábamos con la garganta secahablando sobre el if, me preguntaste como se verifican condiciones. Veamos entonces

El Comando testBien, todos estamos acostumbrados a usar el if para verificar condiciones, y estas condicionessiempre son: mayor, menor, mayor o igual, menor o igual, igual y diferente. En Shell para verificarcondiciones, usamos el comando test, sólo que este es mucho más poderoso de lo que estamoshabituados. Primero te voy a mostrar las principales opciones (existen muchas otras), paraverificar la existencia de archivos en el disco:

Opciones del Comando test para archivos

-x arch arch existe y con derechos de ejecución

Opción Verdadero si: -e arch arch existe -s arch arch existe y tiene tamaño mayor que cero -f arch arch existe y es un archivo regular -d arch arch existe y es un directorio; -r arch arch existe y con derechos de lectura -w arch arch existe y con derechos de escritura

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 34 / 123

Page 35: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Observa ahora las principales opciones para verificar cadenas de caracteres:

Opciones del comando test para cadenas de caracteres

c1 = c2 Cadena c1 y c2 son idénticas

Opción Verdadero si: -z cadena Tamaño de cadena es cero -n cadena Tamaño de cadena es mayor que cero

cadena La cadena cadena tiene tamaño mayor que cero

Y crees que se acabó ahí? Pues estás engañado! Ahora viene la parte a la que estás másacostumbrado, o sea las famosas comparaciones con números. Fijate en la tabla que sigue:

Opciones del comando test para números

n1 -le n2 n1 es menor o igual a n2 less or equal

Opción Verdadero si: Significado n1 -eq n2 n1 y n2 son iguales equal n1 -ne n2 n1 y n2 no son iguales not equal n1 -gt n2 n1 es mayor que n2 greater than n1 -ge n2 n1 es mayor o igual a n2 greater or equal n1 -lt n2 n1 es menor que n2 less than

Además de todo eso, se suman a las opciones que te mostré las siguientes opciones:

Operadores

-o O lógico

Operador Finalidad Paréntesis ( ) Agrupar Admiración ! Negar

-a Y lógico

Ufa! Como viste hay mucha cosa y como te dije al comienzo, nuestro if es mucho más poderosoque los de otros. Vamos a ver en unos ejemplos como funciona todo esto, primero verificaremos laexistencia de un directorio:

Ejemplos:

if test -d lmb then cd lmb else mkdir lmb cd lmb fi

En este ejemplo, verifiqué la existencia de un directorio definido lmb, en caso negativo (else), ésteseria creado. Ya sé, vas a criticar mi razonamiento diciendo que el script no está optimizado. Losé perfectamente, pero quería que entendieras este ejemplo, para poder usar después el signo deadmiración (!) como un negador del test. Mira esto:

if test ! -d lmb then mkdir lmb

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 35 / 123

Page 36: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

fi cd lmb

De esta forma el directorio lmb sería creado solamente si este no existiese, y esta negación sedebe al signo de admiración (!) que precede a la opción -d. Al finalizar la ejecución de estefragmento de script, el programa estaría seguramente dentro del directorio lmb.

Vamos a ver dos ejemplos para entender como se diferencia la comparación entre números yentre cadenas.

cad1=1 cad2=01 if test $cad1 = $cad2 then echo Las variables son iguales. else echo Las variables son diferentes. fi

Ejecutando el fragmento del programa arriba, resulta:

Las variables son diferentes.

Vamos a modificarlo un poco, de manera que la comparación esta vez sea numérica:

cad1=1 cad2=01 if test $cad1 -eq $cad2 then echo Las variables son iguales. else echo Las variables son diferentes. fi

Y lo ejecutamos nuevamente:

Las variables son iguales.

Como viste, en las dos ejecuciones obtuve resultados diferentes porque la cadena 01 es realmentediferente de la cadena 1, sin embargo, la cosa cambia cuando las variables son verificadas enforma numérica, ya que el número 1 es igual al número 01.

Ejemplos:

Para mostrar el uso de los conectores -o (O) y -a (Y), tengo un ejemplo bien grosero, hechodirectamente en el prompt (pido disculpas a los zoólogos, ya que no entendiendo nada de reino,clase, orden, familia, género y especie, puede que lo que estoy llamando familia o género tengagrandes posibilidades de ser incorrecto):

$ Familia=felina $ Genero=gato $ if test $Familia = canina -a $Genero = lobo -o $Familia = felina -a $Genero = leon > then > echo Cuidado > else > echo Se puede acariciar > fi Se puede acariciar

En este ejemplo en caso de que el animal fuera de la familia canina Y (-a) del género lobo, O (-o)de la familia felina Y (-a) del género leon, se daria un aviso de alerta, en caso contrario el mensajesería de incentivo.

Los signos de mayor (>) al inicio de las líneas internas al if son los prompts de continuación (queestán definidos en la variable $PS2) y cuando el Shell identifica que un comando continuará en lalínea siguiente, automáticamente los va colocado, hasta que el comando sea finalizado.

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 36 / 123

Page 37: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Vamos a cambiar el ejemplo para ver si continúa funcionando:

$ Familia=felino $ Genero=gato $ if test $Familia = felino -o $Familia = canino -a $Genero = onza -o $Genero = lobo > then > echo Cuidado! > else > echo Puede acariciar > fi Cuidado!

Obviamente la operación resultó en error, ya que la opción -a tiene prioridad sobre la -o, y así loque se evaluó primero, fué la expresión:

$Familia = canino -a $Genero = onza

Que fué evaluada como falsa, y dió el seguiente resultado:

$Familia = felino -o FALSO -o $Genero = lobo

Que una vez resuelta dió:

VERDADERO -o FALSO -o FALSO

Como ahora todos los conectores son -o, y para que una serie de expresiones conectadas entre sípor diversos O lógicos sea verdadera, basta que una de ellas lo sea, la expresión final resultó comoVERDADERO y el then fue ejecutado de forma incorrecta. Para que vuelva a funcionar hagamos loseguiente:

$ if test \($Familia = felino -o $Familia = canino\) -a \($Genero = onza -o $Genero = lobo\) > then >echo Cuidado! > else > echo Puede acariciar > fi Puede acariciar

De esta forma, con el uso de los paréntesis agrupamos las expresiones con el conector -o, dandoprioridad a sus ejecuciones y resultando:

VERDADERO -a FALSO

Para que sea VERDADERO el resultado de dos expresiones ligadas por el conector -a es necesarioque ambas sean verdaderas, lo que no es el caso del ejemplo arriba citado. Así el resultado finalfue FALSO, siendo entonces el else correctamente ejecutado.

Si quisieramos escojer un CD que tenga músicas de 2 artistas diferentes, nos sentimos tentados ausar un if con el conector -a, pero siempre es bueno recordar que el bash nos dá muchosrecursos y eso podría ser hecho de forma mucho más simple con un único comando grep, de lasiguiente manera:

$ grep Artista1 musicas | grep Artista2

De la misma forma, para escojer CDs que tengan la participación del Artista1 y del Artista2, noes necesario montar un if con el conector -o. El egrep (o grep -E, siendo éste másrecomendable), también nos resuelve eso. Fijate como:

$ egrep (Artista1|Artista2) musicas

O (en ese caso específico) el propio grep puro y simple podría ayudarnos:

$ grep Artista[12] musicas

En el egrep arriba, fue usada una expresión regular, donde la barra vertical (|) trabaja como un Ológico y los paréntesis son usados para limitar la amplitud de éste O.Ya en el grep de la líneasiguiente, la palabra Artista debe ser seguida por alguno de los valores de la lista formada porlos paréntesis rectos ([ ]), o sea, 1 o 2.

- Está bien, acepto el argumento, el if del Shell es mucho más poderoso que los otros

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 37 / 123

Page 38: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Está bien, acepto el argumento, el if del es mucho más poderoso que los otrosconocidos, pero entre nosotros, esa construción del if test ... es muy extraña, y poco legible.

- Si, tienes toda la razón, tampoco me es simpática y me parece que a nadie le gusta. Creo quefue por eso, que el Shell incorporó otra sintáxis que substituye el comando test.

Ejemplos:

Para esto vamos a ver nuevamente aquél ejemplo para cambiar de directorios, que era así:

if test ! -d lmb then mkdir lmb fi cd lmb

y utilizando la nueva sintáxis, vamos a hacerla así:

if [ ! -d lmb ] then mkdir lmb fi cd lmb

O sea, el comando test puede ser substituído por un par de paréntesis rectos ([ ]), separadospor espacios en blanco de los argumentos, lo que aumentará enormemente la legibilidad, pues elcomando if quedara con una sintáxis parecida a de otras lenguajes y por este motivo, usaré elcomando test de esta forma de ahora en adelante.

Querida, Encojieron el Comando Condicional!Si creees que se acabó, estás muy equivocado. Repara en la tabla (booleana) siguiente:

Valores Booleanos Y O

FALSO-FALSO FALSO FALSO

VERDADERO-VERDADERO VERDADERO VERDADERO VERDADERO-FALSO FALSO VERDADERO FALSO-VERDADERO FALSO VERDADERO

O sea, cuando el conector es Y y la primera condición es verdadera, el resultado final puede serVERDADERO o FALSO, dependiendo de la segunda condición, ya en el conector O, en caso que laprimera condición sea verdadera, el resultado siempre será VERDADERO y si la primera fuera falsa,el resultado dependerá de la segunda condición.

Bueno, las personas que crearon el interpretador no son bobas y están siempre intentandooptimizar al máximo los algoritmos. Por tanto, en el caso del conector Y, la segunda condición noserá evaluada, en el caso de que la primera sea falsa, ya que el resultado será siempre FALSO. Yacon el O, la segunda será ejecutada solamente en el caso de que la primera sea falsa.

Aprovechando eso, crearon una forma abreviada de hacer tests. Bautizaron el conector Y de && y elO de || y para ver como funciona esto, vamos a usarlos como test en nuestro viejo ejemplo decambiar de directorio, que en su última versión estaba así:

if [ ! -d lmb ] then mkdir lmb fi cd lmb

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 38 / 123

Page 39: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Eso también podría ser escrito de la siguiente manera:

[ ! -d lmb ] && mkdir lmb cd lmb

O inclusive sacando la negación (!):

[ -d lmb ] || mkdir lmb cd lmb

En el primer caso, si el primer comando (el test que está representado por los paréntesis rectos)estuviera bien resultado, o sea, no existe el directorio lmb, el mkdir será ejecutado, porque laprimera condición era verdadera y el conector era Y.

En el ejemplo siguiente, verificaremos si el directorio lmb existe (en el anterior verificabamos si noexistía) y en caso de que eso fuera verdadero, el mkdir no sería ejecutado porque el conector eraO. Otra forma:

cd lmb || mkdir lmb

En este caso, si el cd diera error, sería creado el directorio lmb pero no sería ejecutado el cd haciadentro de él. Para ejecutar más de un comando de esta forma, es necesario que hagamos unagrupamiento de comandos, eso se consigue con el uso de llaves ({ }). Mira como sería la formacorrecta:

cd lmb || { mkdir lmb cd lmb }

Todavia no está bien, porque en el caso de que el directorio no exista, el cd dará el mensaje deerror correspondiente. Entonces debemos hacer:

cd lmb 2> /dev/null || { mkdir lmb cd lmb }

Como viste, el comando if nos permitió hacer un cd seguro de diversas maneras. Es siemprebueno recordar que el seguro a que me referí, es en lo referente al hecho de que al final de laejecución, tu siempre estarás dentro de lmb, siempre que tengas permisos para entrar en lmb,permisos para crear un directorio en ../lmb, haya espacio en el disco, ...

Y toma test!Piensas que ya se acabó? Gran error! Todavia tenemos una forma de test más. Esta es muybuena porque te permite usar patrones para la comparación. Estos patrones atienden a lasnormas de Generación de Nombres de Archivos (File Name Generation, que son ligeramenteparecidas con las Expresiones Regulares, pero no pueden ser confundidas con éstas). Ladiferencia de sintáxis de este para el test que acabamos de ver, es que este trabaja con dosparêntesis rectos de la siguiente forma:

[[ expresión ]]

Donde expresión es una de las que constan en la tabla siguiente:

Expresiones Condicionales Para Padrones

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 39 / 123

Page 40: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Expresiones Condicionales Para Padrones

expr1 ¦¦ expr2 "O" lógico, Verdadero si expr1 o expr2 fueran Verdaderos

Expresión Retorna cadena == padrón cadena1 = padrón

Verdadero si cadena1 es igual a padrón

cadena1 != padrón Verdadero si cadena1 no es igual a padrón. cadena1 < cadena1 Verdadero si cadena1 está antes de cadena1 alfabéticamente. cadena1 > cadena1 Verdadero si cadena1 está después de cadena1 alfabéticamente expr1 && expr2 "Y" lógico, Verdadero si ambos expr1 y expr2 son Verdaderos

$ echo $H 13 $ [[ $H == [0-9] || $H == 1[0-2] ]] || echo Hora no válida Hora no válida $H=12 $ [[ $H== [0-9] || $H == 1[0-2] ]] || echo Hora no válida $

En este ejemplo,verificamos si el contenido de la variable $H esta comprendido entre cero y nueve([0-9]) o (||) si esta entre diez y doze (1[0-2]), dando un mensaje de error en caso que no seaasi.

Ejemplos:

Para saber si una variable tiene el tamaño de un y solamente un caracter, haz:

$ var=a $ [[ $var == ? ]] && echo var tiene un caracter var tiene un caracter $ var=aa $ [[ $var == ? ]]&& echo var tiene un caracter $

Como puedes imaginar, este uso de patrones para comparación, aumenta mucho el poderío delcomando test. En el inicio de esta conversación, antes del último "choppe", afirmabamos que elcomando if del interpretador Shell es más poderoso que sus similares en otros lenguajes. Ahoraque conocimos todo su espectro de funciones, dime: estas de acuerdo o no con esta afirmación?

Acaso Casa con caseVeamos un ejemplo didáctico: dependiendo del valor de la variable $opc el script deberá ejecutaruna de las opciones: inclusión, exclusión, alteración o fin. Fijate como quedaría este fragmento descript:

if [ $opc -eq 1 ] then inclusión elif [ $opc -eq 2 ] then exclusión elif [ $opc -eq 3 ] then alteración elif [ $opc -eq 4 ] then exit else echo Digite una opción entre 1 y 4 fi

En este ejemplo viste el uso del elif con un else if, esta es una sintáxis válida y aceptada, peropodríamos hacerlo mejor y sería usando el comando case, que tiene la sintáxis siguiente:

case $var in patrón1) cmd1 cmd2 cmdn ;;

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 40 / 123

Page 41: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

patrón2) cmd1 cmd2 cmdn ;; patrónn) cmd1 cmd2 cmdn ;; esac

Donde la variable $var es comparada a los patrones patrón1, ..., patrónn y en el caso de queuno de ellos coincida, el bloque de comandos cmd1, ..., cmdn correspondiente será ejecutadohasta que encuentre un doble punto y coma (;;), en donde el flujo del programa se desviará haciala instrucción inmediatamente siguiente, o sea el esac.

En la formación de los patrones, son aceptados los siguientes caracteres:

Caracteres Para Formacion de Padrones

¦ O lógico

Caracter Significado * Cualquier caracter ocurriendo cero o más veces ? Cualquier caracter ocurriendo una vez

[...] Lista de caracteres

Para mostrar como realmente queda mejor, vamos a repetir el ejemplo anterior, sólo que esta vezusaremos el case y no el if ... elif ... else ... fi.

case $opc in 1) inclusión ;; 2) exclusión ;; 3) alteración ;; 4) exit ;; *) echo Digite una opción entre 1 y 4 esac

Como debes haberte dado cuenta, usé el asterisco como la última opción, o sea, si el asteriscoquiere decir cualquier cosa, entonces servirá para cualquier cosa que no este en el intervalo del 1al 4. Otra cosa a tener en cuenta es que el doble punto y coma no es necesario antes del esac.

Ejemplos:

Vamos ahora a hacer un script más radical. Te dará los buenos días, buenas tardes o buenasnoches, dependiendo de la hora en que sea ejecutado, pero primero mira estos comandos:

$ date Tue Nov 9 19:37:30 BRST 2004 $ date +%H 19

El comando date informa de la fecha completa del sistema, sin embargo tiene diversas opcionespara su enmascaramiento. En este comando, la formatación comienza con un signo de más (+) ylos caracteres de formatación vienen después de un signo de porcentaje (%), así el %H significa lahora del sistema. Dicho esto, vamos al ejemplo:

$ cat bienvenido.sh #!/bin/bash # Programa bien educado que # da los Buenos dias, buenastardes o # las buenas noches dependiendo de la hora Hora=$(date +%H) case $Hora in 0? | 1[01])echo Buenos días ;; 1[2-7] ) echo Buenas tardes ;; * ) echo Buenas noches ;; esac exit

Fue pesado, verdad?. - Que vá!. Vamos a desmenuzar la resolución caso a caso (o sería case-a-case? )

0? | 1[01] - Significa cero seguido de cualquier cosa (?), o (|) uno seguido de cero o uno ([01]) osea, esta línea pegó 01, 02, ... 09, 10 y 11;

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 41 / 123

Page 42: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

1[2-7] - Significa uno seguido de la lista de dos a siete, o sea, esta línea pegó 12, 13, ... 17;

* - Significa todo aquello que no se encuadró en ninguno de los patrones anteriores.

- Mira, hasta ahora hablé mucho y bebí poco. Ahora te voy a pasar un ejercicio para que lohagas en tu casa y me des la respuesta la próxima vez que nos encontremos aqui en el bar, deacuerdo?

- De acuerdo, pero antes informe a las personas que nos están acompañando en este curso,como pueden hacerlo para encontrarle, para hacerle críticas, hacer chistes, invitarle a una cerveza,un curso, unas charlas o hasta si quieren, para hablar mal de los políticos.

- es fácil, mi e-mail es [email protected], pero para de distraerme, que no me voy a olvidarde pasarte el script de los deberes, y este es: quiero que hagas un programa que recibirá comoparámetro el nombre de un archivo y que cuando sea ejecutado grabe este archivo con el nombreoriginal seguido de una tilde (~), además colocaras este archivo dentro del vi (de paso, el mejoreditor del cual se tiene noticia) para ser editado. Esto sirve para tener siempre la última copiabuena del archivo en el caso de que la persona haga alteraciones indebidas. Obviamente, tendrásque hacer las investigaciones necesarias, como verificar si fué pasado un parámetro, si el archivoque fué pasado existe, ... En fin, lo que se te venga a la cabeza y tu creas que deba estar en elscript. Entendiste?

- Hum, hum...

- Chico! Traeme otra más sin espuma, que aquí el amigo ya está pensando!

Gracias y hasta la próxima

-- HumbertoPina - 05 Oct 2006

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa004 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa004 42 / 123

Page 43: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa005Controles: - Última Atualização: [14 Feb 2012 - V.6]

Conversación de bar Parte VComandos de Loop (o lazo)

El comando forPrimera sintaxis del comando for:Segunda sintáxis del comando for:Tercera sintáxis del comando for:

- Que hay amigo! Ordenaste ya tus ideas?, Se fundió ya tu cabeza, o todavía aguanta másShell?

- Aguanto, claro! Me esta gustando mucho! Me gustó tanto que hasta me esmeré en el ejercicio

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 43 / 123

Page 44: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

que me dejaste. Te acuerdas que me pediste que hiciera un programa que recibiría comoparámetro el nombre de un archivo y que cuando se ejecutara salvara este archivo con el nombreoriginal seguido de una tilde (~) ademas de colocarlo dentro del vi?

- Claro que me acuerdo, muéstramelo y explica como lo hiciste.

$ cat vira #!/bin/bash # # vira - vi grabando el archivo anterior # == = =

# Verificando si fue pasado 1 parámetro if [ "$#" -ne 1 ] then echo "Erro -> Uso: $0 " exit 1 fi

Arq=$1 # En caso de que el archivo no exista, no hay copia para grabar if [ ! -f "$Arq" ] then vi $Arqexit 0 fi

# Si no puedo alterar el archivo, para que voy a usar el vi? if [ ! -w "$Arq" ] then echo "Usted notiene privilegios de grabación en $Arq" exit 2 fi

# Ya que está todo OK, voy a salvar la copia y llamar el vi cp -f $Arq $Arq~ vi $Arq exit 0

- Bárbaro, muy bien! Pero dime una cosa: porque terminaste el programa con un exit 0?

- Ahhh! Descubri que el número después del exit da el código de retorno del programa (o $?, teacuerdas?), y de esta forma, si todo se ejecuto bien, se cerraría con el $? = 0. Sin embargo, siobservas, verás que en el caso de que el programa no reciba el nombre del archivo o en el casode que el operador no tenga privilegios de grabación sobre este archivo, el código de retorno ($?)sería diferente de cero.

- Grande!, aprendiste bien, pero es bueno dejar claro que el exit 0, simplemente exit, o nocolocar exit, producen igualmente un código de retorno ($?) igual a cero, si el programa fue bienejecutado. Ahora vamos a hablar sobre las instrucciones de loop o lazo, pero antes voy a pasar elconcepto de bloque de programa.

Hasta ahora ya vimos algunos bloques de programa, como cuando te mostré un ejemplo parahacer un cd hacia dentro de un directorio, y que era así:

cd lmb 2> /dev/null || { mkdir lmb cd lmb }

El fragmento contenido entre las dos llaves ({}), forma un bloque de comandos. También en esteejercicio que acabamos de ver, en que salvamos el archivo antes de editarlo, existen varios bloquede comandos comprendidos entre los then y los fi del if.

Un bloque de comandos también puede estar dentro de un case, o entre un do y un done.

- Espera ahí Julio, que do y done son esos, no me acuerdo de que hayas hablado de ellos y miraque estoy prestando mucha atención...

- Claro, todavía no había hablado de ellos porque no había llegado el momento adecuado.Todas las instrucciones de loop o lazo, ejecutan los comandos del bloque comprendido entre el doy el done.

Comandos de Loop (o lazo)Las instrucciones de loop o lazo son el for, el while y el until que pasaré a explicarte una a una apartir de hoy.

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 44 / 123

Page 45: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

El comando for

Si ya estás habituado a programar, con seguridad conoces el comando for, pero lo que no sabeses que el for, que es una instrucción intrínseca del Shell (esto significa que el código fuente delcomando es parte del código fuente del Shell, o sea en buen idioma "programés" es un built-in),es mucho más poderoso que los semejantes de otras lenguajes.

Vamos a entender su sintaxis, primero en español y después como funciona realmente.

para var en val1 val2 ... valn haga cmd1 cmd2 cmdn hecho

Donde la variable var asume cada uno de los valores de la lista val1 val2 ... valn y para cadauno de esos valores ejecuta el bloque de comandos formado por cmd1, cmd2 y cmdn

Ahora que ya vimos el significado de la instrucción en español, veamos la sintaxis correcta:

Primera sintaxis del comando for:

for var in val1 val2 ... valn do cmd1 cmd2 cmdn done

Vamos directo a los ejemplos, a fin de entender el funcionamiento de este comando. Vamos aescribir un script para listar todos los archivos de nuestro directorio separados por dos puntos,pero mira primero:

$ echo * ArchDoDOS.txt1 confuso incusu logado musexc musicas musinc muslist

O sea, el Shell vio el asterisco (*), lo expandió con el nombre de todos los archivos del directorio yel comando echo los mostró en la pantalla separados por espacios en blanco. Visto esto vamos aver como resolver el problema que nos propusimos:

$ cat testefor1 #!/bin/bash # 1o. Prog didáctico para entender el for

for Arch in * do echo -n $Arq: # La opción -n es para no saltar la línea done

Ahora vamos a ejecutarlo:

$ testefor1 ArchDoDOS.txt1:confuso:incusu:logado:musexc:musicas:musinc:muslist:$

Como viste, el Shell transformó el asterisco (es odioso ser llamado por un asterisco) en una listade archivos separados por espacios en blanco. cuando el for vio aquella lista, se dijo: "Opa!, listaseparadas por espacios es mi especialidad!"

El bloque de comandos para ejecutar era solamente el echo, que con la opción -n listó la variable$Arch seguida de dos puntos (:), sin saltar de línea. El signo de ($) del final de la línea de ejecuciónes el prompt. que permaneció en la misma línea también en función de la opción -n. Otro ejemplosimple (por ahora):

$ cat testefor2 #!/bin/bash # 2o. Prog didáctico para entender el for

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 45 / 123

Page 46: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

for Palabra in Conversa de Bar do echo $Palabra done

Y ejecutando resulta:

$ testefor2 Conversa de Bar

Como viste, este ejemplo es tan bobo y simple como el anterior, pero sirve para mostrar elcomportamiento básico del for.

Fíjate en la fuerza del for: todavía estamos en la primera sintaxis del comando y ya estoymostrando nuevas formas de usarlo. Allá atrás, te había hablado que el for usaba listas separadaspor espacios en blanco, pero eso es una verdad a medias, era sólo para facilitar la compresión.

En realidad, las listas no son obligatoriamente separadas por espacios, pero antes de seguir,déjame mostrarte como se comporta una variable del sistema llamada $IFS. Observa sucontenido:

$ echo "$IFS" | od -h 0000000 0920 0a0a 0000004

O sea, mandé la variable (protegida de la interpretación del Shell por las comillas) para un dumphexadecimal (od -h) y resultó:

Contenido de la Variable $IFS

0a <ENTER>

Hexadecimal Significado 09 <TAB>

20 <ESPACIO>

Donde el último 0a fue originado por el <ENTER> dado al final del comando. Para mejorar laexplicación, vamos a ver eso de otra forma:

$ echo ":$IFS:" | cat -vet : ^I$ :$

Presta atención a lo siguiente para entender la construcción del comando cat:

En el comando cat, la opción -e representa el <ENTER> como un signo de pesos ($) y la opción -trepresenta el <TAB> como un ^I. Usé los dos puntos (:) para mostrar el inicio y el fin del echo. y deesta forma, otra vez podemos notar que los tres caracteres están presentes en aquella variable.

Ahora, IFS significa Inter Field Separator o, traduciendo, separador entre campos. Una vezentendido eso, puedo afirmar (porque lo voy a probar) que el comando for no usa listasseparadas por espacios en blanco, sino por el contenido de la variable $IFS, cuyo valor pordefecto (default) son esos caracteres que acabamos de ver. Para comprobarlo, vamos a mostrarun script que recibe el nombre del artista como parámetro y lista las músicas que este ejecuta,pero primero veremos como está nuestro archivo musicas:

$ cat musicas album 1^Artista1~Musica1:Artista2~Musica2 album2^Artista3~Musica3:Artista4~Musica4 album 3^Artista5~Musica5:Artista6~Musica6 album4^Artista7~Musica7:Artista1~Musica3 album 5^Artista9~Musica9:Artista10~Musica10

En base a este esquema mostrado arriba fue desarrollado el script que sigue:

$ cat listartista #!/bin/bash # Dado un artista, muestra sus músicas

if [ $# -ne 1 ] then echo Usted debería haber pasado un parámetro exit 1 fi

IFS=" :"

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 46 / 123

Page 47: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

for ArtMus in $(cut -f2 -d^ musicas) do echo "$ArtMus" | grep $1 && echo $ArtMus | cut -f2 -d~ done

El script, como siempre, comienza testando si los parámetros fueron pasados correctamente, enseguida el IFS fue configurado para <ENTER> y dos puntos (:) (como demuestran las comillas enlíneas diferentes), porque es él el que separa los bloques Artistan~Musicam. De esta forma, lavariable $ArtMus irá a recibir cada uno de estos bloques del archivo (observa que el for ya recibelos registros sin el álbum en virtud del cut en su línea). En el caso de que encuentre el parámetro($1) en el bloque, el segundo cut listará solamente el nombre de la música. Vamos a ejecutarlo:

$ listartista Artista1 Artista1~Musica1 Musica1 Artista1~Musica3 Musica3 Artista10~Musica10Musica10

Epa! Pasaron dos cosas no deseadas: los bloques también fueron listados y la Musica10 también.Además de eso, nuestro archivo de músicas es muy simple, en la vida real, tanto la música comoel artista tienen más de un nombre. Suponte que el artista fuera una dupla de música folclóricallamada Clitandro & Eduviges (no me atrevo ni a dar la idea, por miedo a que se haga realidad ).En este caso el $1 sería Clitandro y el resto de este lindo nombre sería ignorado en la búsqueda.

Para que eso no ocurriese, debería pasar el nombre del artista entre comillas (") o alterar $1 por $@(que significa todos los parámetros pasados), que es la mejor solución, pero en este caso tendríaque modificar la crítica de los parámetros y el grep. La nueva crítica no actuaria si yo pasase unparámetro, o por lo menos un parámetro y en cuanto al grep, mira lo que resultaría después de lasubstitución del $* (que entraría en lugar del $1) por los parámetros:

echo "$ArtMus" | grep clitandro & eduviges

Lo que resultaría en un error. Lo corretco sería:

echo "$ArtMus" | grep -i "clitandro & eduviges"

Donde fue colocada la opción -i para que la búsqueda ignorase mayúsculas y minúsculas y lascomillas también fueron insertadas para que el nombre del artista fuera visto como una cadenaúnica y monolítica.

Todavia falta arreglar el error de haber listado al Artista10. Para esto, lo mejor es decirle al grepque la cadena está en el início ( forma cuya expresión regular es ^) de $ArtMus y luego despuésviene una tilde (~). Es necesario también que se redireccione la salida del grep para /dev/nullpara que los bloques no sean listados más . Observa entonces la nueva (y definitiva) cara delprograma:

$ cat listartista #!/bin/bash # Dado un artista, muestra sus músicas # versao 2

if [ $# -eq 0 ] then echo Usted debería haber pasado un parámetro exit 1 fi

IFS=" :"

for ArtMus in $(cut -f2 -d^ musicas) do echo "$ArtMus" | grep -i "^$@~" > /dev/null && echo $ArtMus| cut -f2 -d~ done

Que ejecutado da:

$ listartista Artista1 Musica1 Musica3

Segunda sintáxis del comando for:

for var do

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 47 / 123

Page 48: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

cmd1 cmd2 cmdn done

- Espera ahí!, sin el in como va a saber que valor asumir?

- Eso mismo, no? Esta construcción a primera vista parece extraña pero es bastante simple. Eneste caso, var asumirá uno a uno cada uno de los parámetros pasados para el progama.

Vamos rapidito a los ejemplos para entenderlo mejor. Vamos a hacer un script que reciba comoparámetro una cantidad de músicas y liste sus autores:

$ cat listamusica #!/bin/bash # Recibe parte de los nombres de músicas como parámetro y # listalos intérpretes. Si el nombre es compuesto, debe # ser pasado entre comillas. # ex. "No soy tuperrito, no!" "Asadito de Madre" # if [ $# -eq 0 ] then echo Uso: $0 musica1 [musica2] ... [musican]exit 1 fi IFS=" :" for Musica do echo $Musica Str=$(grep -i "$Musica" musicas) || { echo " Noencontrada" continue } for ArtMus in $(echo "$Str" | cut -f2 -d^) do echo " $ArtMus" | grep -i"$Musica" | cut -f1 -d~ done done

De la misma forma que los otros, comenzamos el ejercício con una crítica sobre los parámetrosrecibidos, en seguida hicimos un for en que la varible $Musica recibirá cada uno de losparámetros pasados, colocando en $Str todos los álbums que contienen las músicas pasadas. Enseguida, el otro for coge cada bloque Artista~Musica de los registros que están en $Str y listacada artista que ejecute aquella música.

Como siempre vamos a ejecutarlo para ver si realmente funciona:

$ listamusica musica3 Musica4 "Yegüita Pocotó" musica3 Artista3 Artista1 Musica4 Artista4Yegüita Pocotó No encontrada

La lista quedó fea porque todavia no sabemos dar formato a la salida, pero cualquier día de estos,cuando sepas posicionar el cursor, hacer negritas, trabajar con colores, etc, haremos esta listanuevamente usando todas estas perfumerías y entoces quedará bien coqueto.

A esta altura de los acontecimientos debes estar preguntandote: "Y aquél for tradicional de losotros lenguajes en que sale contando a partir de un número, con un determinado incremento hastaalcanzar una condición?"

Y es ahí donde te respondo: "Yo no te dije que nuestro for es más completo que los otros?" Parahacer eso existen dos formas:

1 - con la primera sintáxis que vimos, como en los siguientes ejemplos directamente en el prompt:

$ for i in $(seq 9) > do > echo -n "$i " > done 1 2 3 4 5 6 7 8 9

Aquí, la variable i asumió los enteros del 1 al 9 generados por el comando seq y la opción -n delecho fue usada para no saltar de línea con cada número listado (me siento ecologicamentecorrecto por no gastar una cantidad de papel de la revista cuando eso puede ser evitado).Además usando el for con seq:

$ for i in $(seq 3 9) > do > echo -n "$i " > done 4 5 6 7 8 9

O todavia en la forma más completa del seq:

$ for i in $(seq 0 3 9) > do > echo -n "$i " > done 0 3 6 9

2 – La otra forma de hacer lo deseado es con una sintáxis muy parecida al for del lenguaje C,

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 48 / 123

Page 49: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

como veremos a continuación.

Tercera sintáxis del comando for:

for ((var=ini; cond; incr)) do cmd1 cmd2 cmdn done

Donde:

var=ini - Significa que la variable var comenzará a partir de un valor inicial ini;cond - Significa que el loop o lazo del for será ejecutado en cuanto la var no cumpla lacondición cond;incr - Significa el incremento que la variable var sufrirá en cada pasada del loop.

Como siempre vamos a los ejemplos y la cosa quedara más fácil:

$ for ((i=1; i<=9; i++)) > do > echo -n "$i " > done 1 2 3 4 5 6 7 8 9

En este caso la variable i partió del valor inicial 1, el bloque de comando (aqui solamente el echo)será ejecutado en cuanto i sea menor o igual (<=) a 9 y el incremento de i será de 1 a cadapasada del loop.

Fíjate que en el for propiamente dicho (y no en el bloque de comandos) no coloqué un signo depesos ($) antes del i, y la notación para incrementar (i++) es diferente de la que vimos hastaahora. Esto es porque el uso de paréntesis dobles (así como el comando let) llama elinterpretador aritmético del Shell, que es más tolerante.

Como me referí al comando let, y sólo para mostrar como funciona, vamos hacer lo mismo,omitiendo sin embargo, la última parte del for, pasándola hacia el bloque de comandos, asíademas veras la versatilidad del for.

$ for ((; i<=9;)) > do > let i++ > echo -n "$i " > done 1 2 3 4 5 6 7 8 9

Observa que el incremento desapareció del cuerpo del for y pasó dentro del bloque de comandos,fíjate también que cuando usé el let, no fue necesario siquiera inicializar la varible $i. Observa lossiguientes comandos escritos directamente en el_prompt_ para mostrar lo que acabo de decir:

$ echo $j

$ let j++ $ echo $j 1

O sea, la variable $j ni siquiera existía y en el primero let asumió el valor 0 (cero) para, despuésdel incremento, tener el valor 1.

Fíjate en lo simples que son las cosas:

$ for arq in * > do > let i++ > echo "$i -> $Arq" > done 1 -> ArqDoDOS.txt1 2 -> confuso 3 -> incusu4 -> listamusica 5 -> listartista 6 -> logado 7 -> musexc 8 -> musicas 9 -> musinc 10 -> muslist 11 -> testefor1 12 -> testefor2

- Y hasta aqui amigo!, tengo la seguridad que hoy tomaste una buena dosis de jarabe delcomando for. Por hoy es suficiente, la próxima vez que nos encontremos hablaremos sobre otrasinstruciones de loop, pero me gustaria que hasta entonces, hicieses un pequeño script para contarla cantidad de palabras de un archivo texto, cuyo nombre sería recibido por parámetro.

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 49 / 123

Page 50: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

OBS: Esa cuenta tiene que ser hecha usando el comando for para que te habitues a su uso. Novale usar o wc -w.

- Chico! Tráeme, por favor la del estribo!

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar también para mandar mi aviso publicitario: puedes decirle a los amigos que quienquiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 20 Oct 2006

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa005 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa005 50 / 123

Page 51: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa006Controles: - Última Atualização: [14 Feb 2012 - V.6]

Conversación de Bar Parte VIComandos de Loop o Lazo (Continuación)

Un Poco más de for y MatemáticaEl comando whileEl comando untilAtajos en loop

Comandos de Loop o Lazo (Continuación) - Que tal amigo mio, como estas? Ya lo sabes todo acerca del comando for?. Te dejé unejercicio de deberes, y si no estoy equivocado, era para contar la cantidad de palabras de un

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 51 / 123

Page 52: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

ejercicio de deberes, y si no estoy equivocado, era para contar la cantidad de palabras de unarchivo... Lo hiciste?

- Claro! Estoy entusiasmando con ese lenguaje del shell, lo hice de la forma que me pediste, osea sin usar el comando wc porque si no era mucho más fácil. Mira lo que hice...

- Epa! Un momento! Realmente estás entusiasmando con el lenguaje, pero yo estoy deseandotomar un "chopp". Chico!, tráeme dos por favor. Uno sin espuma, como siempre!

- Como te iba diciendo, mira lo que hice. Es muy fácil...

$ cat contpal.sh #!/bin/bash # Script meramente pedagógico cuya # función es contar la cantidadde palabras # de un archivo. Se supone que las # palabras están separadas entre sí # porespacio, o .

if [ $# -ne 1 ] then echo uso: $0 /camino/del/archivo exit 2 fi Cont=0 for Palabra in $(cat $1) doCont=$((Cont+1)) done echo El archivo $1 tiene $Cont palabras.

O sea, el programa comienza como siempre verificando si el pasaje de parámetros fue correcto,en seguida el comando for se encarga de coger cada una de las palabras (recuerda que el $IFSpatrón (default) es espacio, <TAB> y <ENTER>, que es exactamente lo que deseamos para separarlas palabras), incrementando la variable $Cont.

Vamos a recordar como es el archivo ArchDelDOS.txt.

$ cat ArqDoDOS.txt Este archivo fue generado por el DOS/Rwin y fue bajado por un ftp mal hecho.

Ahora vamos a testear el programa pasando este archivo como parámetro:

$ contpal.sh ArchDelDOS.txt El archivo ArchDelDOS.txt tiene 15 palabras

- Muy bien! funcionó correctamente!

Un Poco más de for y Matemática

Volviendo a nuestro tema, la última vez que estuvimos aquí, terminamos nuestra conversaciónmostrando el loop del for a continuación:

for ((; i<=9;)) do let i++ echo -n "$i " done

Una vez que llegamos a este punto, creo que sera bastante interesante citar que el Shell trabajacon el concepto de "Expansión Aritmética" (Arithmetic Expansion) que es accionado por unaconstrucción de la forma

$((expresión))

o

let expressión

En el último for citado, usé la expansión de las dos formas, pero no podríamos seguir adelante sinsaber que la expresión puede ser una de las listadas a continuación:

Expansión Aritmética

Expresión Resultado

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 52 / 123

Page 53: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

|| O lógico

id++ id-- pós-incremento y pós-decremento de variables++id -–id pré-incremento y pré-decremento de variables

** exponenciación* / % multiplicación, división, resto de la división+ - adición, substraccuión

<= >= < > comparación== != igualdad, desigualdad&& Y lógico

- Y si piensas que la conversación sobre loop (o lazo) se termina en el comando for, estas enun gran error amigo, vamos a partir de ahora a ver dos más.

El comando while

Todos los programadores conocen este comando, ya que es común a todas los lenguajes y enellos lo que normalmente ocurre, es que un bloque de comandos es ejecutado, en cuanto que (encuanto que en inglés es while) una determinada condición sea verdadera. Pues bien, esto es loque ocurre en otros lenguajes! En programación Shell, el bloque de comandos es ejecutado encuanto que un comando sea verdadero. Y esta claro, si quisiera verificar una condición, usaria elcomando while junto con el comando test, exactamente como aprendiste a hacer en el if,recuerdas?

Entonces, la sintaxis del comando queda de la siguiente forma:

while comando do cmd1 cmd2 ... cmdn done

y de esta forma el bloque de comandos formado por las instrucciones cmd1, cmd2,... y cmdn seráejecutado en cuanto que la ejecución de la instrucción comando sea ejecutada con éxito.

Suponga la siguiente escena: tengo una tremenda gatita esperándome y estoy preso en el trabajosin poder salir porque mi jefe, que es un tremendo rompe cocos se encuentra todavía trabajandoen su escritorio, que queda bien en medio de mi salida a la calle.

Él empezó a tener las antenas (probablemente instaladas en su cabeza por la esposa) atentas,después de la quinta vez que me vio pasar por su puerta y ver que todavía estaba allí. Volví a mimesa e hice un script en el servidor de esta forma:

$ cat logaute.sh #!/bin/bash

while who | grep jefe do sleep 30 done echo El rompe se fue, no te entretengas, date el piro y vesenfrente

En este scriptiziño, el comando while verifica el pipeline compuesto por el who y por el grep y queserá verdadero en cuanto el grep localice la palabra jefe en la salida del who. Así, el script dormirádurante 30 segundos mientras el jefe esté logado (Argh!). Cuando él se desconecte del servidor, elflujo del script saldra del loop y dará el tan ansiado mensaje de libertad.

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 53 / 123

Page 54: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Si lo ejecuto adivinas lo que pasa?

$ logaute.sh jefe pts/0 Jan 4 08:46 (10.2.4.144) jefe pts/0 Jan 4 08:47 (10.2.4.144) ... jefe pts/0 Jan4 08:52 (10.2.4.144)

Pues que cada 30 segundos es enviada a mi pantalla la salida del grep, lo que no es deseable yaque lleno la pantalla del computador y ademas el esperado mensaje podría pasar desapercibido.Para evitar eso ya sabemos que la salida del pipeline tiene que ser redireccionada hacia/dev/null.

$ cat logaute.sh #!/bin/bash

while who | grep jefe > /dev/null do sleep 30 done echo El rompe se fue, no te entretengas, date elpiro y ves enfrentee

Ahora quiero montar un script que reciba el nombre (y eventuales parámetros) de un programa queserá ejecutado en background y que me informe de su término. Pero, para entender este ejemplo,primero tengo que mostrar una nueva variable del sistema. Mira estos comandos escritosdirectamente en el prompt:

$ sleep 10& [1] 16317 $ echo $! 16317 [1]+ Done sleep 10 $ echo $! 16317

O sea, lance un proceso en background que se ejecutara cada 10 segundos, solo para mostrarque la variable $! guarda el PID (Process IDentification) del último proceso en background, sinembargo, fíjate que después de la línea del done, la variable continuó con el mismo valor.

Bien, sabiendo eso es más fácil de controlar cualquier proceso en background. Observa como:

$ cat monbg.sh #!/bin/bash

# Ejecuta y controla un # proceso en background

$1 & # Coloca en backgroud while ps | grep -q $! do sleep 5 done echo Fin del Proceso $1

Este script es bastante similar al anterior, pero tiene unos trucos más, mira: tiene que serejecutado en background para no retener el prompt pero el $! será el del programa pasado comoparámetro ya que fue colocado en background después del monbg.sh propiamente dicho. Observatambién la opción -q (quiet) del grep, sirve para transformarlo en un comando silencioso, o sea,para que el grep trabaje de forma invisible. Este mismo resultado podría ser obtenido si la líneafuera while ps | grep $! > /dev/null, como en los ejemplos que vimos hasta ahora.

No te olvides: el Bash dispone de la variable $! que posee el PID (Process IDentification) delúltimo proceso ejecutado en background.

Vamos a mejorar el musinc, que es nuestro programa para incluir registros en el archivo musicas,pero antes necesito enseñarte a capturar un dato de la pantalla, y ya voy avisando: solo voy a daruna pequeña parte del comando read (que es quien hace la captura de la pantalla) que sea losuficiente para resolver este problema. En otra vuelta de "chopp" te lo voy a enseñar todo delasunto, inclusive como formatear la pantalla, pero hoy estamos hablando sobre loops.

La sintaxis del comando read que nos interesa por ahora es la siguiente:

$ read -p "prompt de lectura" var

Donde prompt de lectura es el texto que quieres que aparezca escrito en la pantalla, y cuando eloperador escriba el dato éste irá a parar anla variable var. Por ejemplo:

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 54 / 123

Page 55: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ read -p "Título del Álbun: " Tit

Bien, una vez entendido eso, vamos a la especificación de nuestro problema: haremos unprograma que inicialmente leerá el nombre del álbum y en seguida hará un loop de lectura,extrayendo la música y el artista. Este loop termina cuando se encuentre una música vacía, o sea,al ser solicitada la escritura de la música, el operador de un simple <ENTER>. Para facilitar la vidadel operador, vamos a ofrecer como default el mismo nombre del artista de la música anterior (yaque es normal que el álbum sea todo del mismo artista) hasta que él desee alterarlo. Vamos a vercomo quedó ahora:

$ cat musinc #!/bin/bash # Catastra CDs (versión 4) # clear read -p "Título del Álbun: " Tit [ "$Tit" ] ||exit 1 # Fin de la ejecución si el título= vacio if grep "^$Tit\^" musicas > /dev/null then echo Esteálbum ya está catastrado exit 1 fi Reg="$Tit^" Cont=1 oArt= while true do echo Datos de la pista$Cont: read -p "Música: " Mus [ "$Mus" ] || break # Sale si vacio read -p "Artista: $oArt // " Art ["$Art" ] && oArt="$Art" # Si vacio Art anterior Reg="$Reg$oArt~$Mus:" # Montando el registroCont=$((Cont + 1)) # La linea anterior también podria ser ((Cont++)) done echo "$Reg" >> musicassort musicas -o musicas

Este ejemplo, comienza con la lectura del título del álbum, y si no es introducido, terminará laejecución del programa. En seguida un grep busca en el inicio (^) de cada registro de músicas, eltítulo introducido seguido del separador (^) (que está precedido de una contrabarra (\) paraprotegerlo de la interpretación del Shell).

Para leer los nombres de los artistas y las músicas del álbum, fue montado un loop de whilesimple, que lo único que tiene a destacar es el hecho de estar almacenando el artista de la músicaanterior en la variable $oArt que solamente tendrá su contenido alterado, cuando alguno de losdatos sea introducido en la variable $Art, o sea, cuando no se teclee un simple <ENTER> paramantener el artista anterior.

Lo viste hasta ahora sobre el while fue muy poco. Este comando es muy utilizado, principalmentepara lectura de archivos, sin embargo nos faltan conocimientos para continuar. Después queaprendamos a leer, veremos esta instrucción más a fondo.

Lectura del archivo significa leer uno a uno todos los registros, lo que es siempre una operaciónlenta. Estate atento de no usar el while cuando su uso puede ser evitado. El Shell tiene recursoscomo el sed y la familia grep que buscan en los archivos de forma optimizada sin ser necesario eluso de comandos de loop para hacerlo registro a registro (o hasta palabra a palabra).

El comando until

El comando until funciona exactamente igual al while, sin embargo al revés. Dije todo pero nodije nada, no es cierto? Es lo siguiente: ambos verifican comandos; ambos poseen la mismasintaxis y ambos actuan en loop, sin embargo, mientras el while ejecuta el bloque de instruccionesdel loop mientras un comando este bien ejecutado, el until ejecuta el bloque del loop hasta queel comando este bien ejecutado. Parece una diferencia insignificante, pero en cambio esfundamental.

La sintáxis del comando es practicamente la misma del while. Observa:

until comando do cmd1 cmd2 ... cmdn done

Y así el bloque de comandos formado por las instruciones cmd1, cmd2,... y cmdn es ejecutado hasta

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 55 / 123

Page 56: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

que la ejecución de la instrucción comando sea bien ejecutada.

Como te dije, el while y el until funcionan de forma antagónica lo cual es muy fácil de demostrar:en una guerra siempre que se inventa una arma, el enemigo busca una solución para neutralizarla.Basado en este principio de la guerra es que mi jefe, creó en el mismo servidor que yo ejecutabael logaute.sh un script para controlar el horário de mi llegada.

Un dia ocurrió un problema en la red, y él me pidió que echara una mirada en su ordenador y medejó solo en su sala. Inmediatamente comencé a revisar sus archivos - porque guerra es guerra - ymira lo que descubrí:

$cat llegada.sh #!/bin/bash

until who | grep julio do sleep 30 done echo $(date "+ El %d/%m a las %H:%Mh") >> haragán.log

Que cara dura! Él estaba montando un log con los horarios en que yo llegaba, y además llamó elarchivo que me controlaba de haragán.log! Que será lo que quiso decir con eso?

En este script, el pipeline who | grep julio, será bien ejecutado solamente cuando julio seaencontrado en el comando who, o sea, cuando yo me "logue" en el servidor. Hasta que eso pase, elcomando sleep, que forma el bloque de instrucciones del until, pondrá el programa en esperapor 30 segundos. Cuando este loop se cierre, será enviado un mensaje hacia el haragán.log(ARGHH!). Suponiendo que el dia 20/01 yo me loguease a las 11:23 horas, el mensaje sería elsiguiente:

El 20/01 a las 11:23h

Cuando vamos a buscar introducir, lo ideal sería que pudiésemos introducir diversos CDs, y en laúltima versión que hicimos del musinc, eso no ocurre, a cada CD que introducimos el programatermina. Veamos como mejorarlo:

$ cat musinc #!/bin/bash # Catastra CDs (versión 5) # Para= until [ "$Para" ] do clear read -p "Títulodel Álbum: " Tit if [ ! "$Tit" ] # Si titulo vacio... then Para=1 # Activé flag de salida else if grep"^$Tit\^" musicas > /dev/null then echo Este álbum ya está introducido exit 1 fi Reg="$Tit^" Cont=1oArt= while [ "$Tit" ] do echo Dados de la pista $Cont: read -p "Música: " Mus [ "$Mus" ] || break #Sale si vacío read -p "Artista: $oArt // " Art [ "$Art" ] && oArt="$Art" # Si vacío Art anteriorReg="$Reg$oArt~$Mus:" # Montando registro Cont=$((Cont + 1)) # La linha anterior tambiénpodría ser ((Cont++)) done echo "$Reg" >> musicas sort musicas -o musicas fi done

En esta versión, fué agregado un loop mayor antes de la lectura del título, que solo terminarácuando la variable $Para deje de estar vacía. En el caso de que el título del álbum no se encuentre,la variable $Para recibirá valor (en este caso coloqué 1 pero podría haber colocado cualquier cosa.Lo importante es que no este vacía) para salir de este loop, y terminar el programa. En el resto, elscript es idéntico a la versión anterior.

Atajos en loop

No siempre un ciclo de programa, comprendido entre un do y un done, sale por la puerta del frente.En algunas oportunidades, tenemos que colocar un comando que aborte de forma controlada esteloop. Al contrario, algunas veces deseamos que el fluxo de ejecución del programa vuelva antes dellegar al done. Para esto, tenemos respectivamente, los comandos break (que ya vimosrápidamente en los ejemplos del comado while) y continue, que funcionan de la siguiente forma:

Lo que no habia mencionado anteriormente es que en sus sintáxis genéricas, aparecen de lasiguiente forma:

break [ctd loop]

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 56 / 123

Page 57: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

y

continue [ctd loop]

Donde ctd loop representa la cantidad de loops internos sobre los que estos comandos van aactuar. Su valor default es 1.

Dudo mucho que nunca hayas borrado un archivo y enseguida no te dieras un golpe en la cabeza,maldiciéndote porque no debías haberlo hecho. Claro, pues yo la décima vez que hice esaburrada, cree un script para simular una cesta de basura, o sea, cuando mando borrar uno (ovarios) archivo(s), el programa "finge" que lo borró, pero en realidad lo que hizo fue mandarlo(s)para el diretório /tmp/LoginName_del_usuario. Llamé este programa de erreeme y en el/etc/profile coloqué la siguiente línea:

alias rm=erreeme

El programa era así:

$ cat erreeme #/bin/bash # # Salvando Copia del Archivo Antes de Borrarlo #

if [ $# -eq 0 ] # Tiene que haber uno o mas archivos para borrar then echo "Erro -> Uso: erreemearch [arch] ... [arch]" echo " El uso de metacaracteres es permitido. Ej. erreeme arch*" exit 1 fi

MiDir="/tmp/$LOGNAME" # Variable del sist. Contiene el nombre del usuario. if [ ! -d $MiDir ] # Sino exister mi directorio bajo el /tmp... then mkdir $MiDir # Voy a crearlo fi

if [ ! -w $MiDir ] # Si no puedo grabar en el directorio... then echo Imposible grabar archivos en$MiDir. Cambie el permiso... exit 2 fi

Erro=0 # Variable para indicar el cod. de retorno del prg for Arch # For sin el "in" recibe losparámetros pasados do if [ ! -f $Arch ] # Si este archivo no existe... then echo $Arch no existe.Erro=3 continue # Vuelve para el comando for fi

DirOrig=`dirname $Arch` # Cmd. dirname informa nombre del dir de $Arch if [ ! -w $DirOrig ] #Verifica permiso de grabación en el directorio then echo Sin permiso de borrar en el directorio de$Arch Erro=4 continue # Vuelve para el comando for fi

if [ "$DirOrig" = "$MiDir" ] # Si estoy "vaciando la basurera"... then echo $Arch quedará sin copiade seguridad rm -i $Arch # Pregunta antes de borrar [ -f $Arch ] || echo $Arch borrado # Será queel usuario lo borró? continue fi

cd $DirOrig # Guardo al final del archivo su directorio pwd >> $Arch # original para usarlo en unscript de undelete mv $Arch $MiDir # Grabo y borro echo $Arch borrado done exit $Erro # Pasoeventual número de error para el código de retorno

Como puedes ver, la mayor parte del script es formada por pequeñas críticas a los parámetroshallados, pero como el script puede haber recibido diversos archivos para borrar, a cada archivoque no se encaja dentro del especificado, hay un continue, para que la secuencia vuelva para elloop del for de forma que pueda recibir otros archivos.

Cuando estás en Windows (con perdón de la mala palabra) y tratas de borrar aquella cantidad debasura con nombres extraños como HD04TG.TMP, si te da un error en uno de ellos, los otros noserán borrados, no es así? Entonces, el continue fue usado para evitar que un barbaridad deestas ocurra, o sea, aunque dé un error en el borrado de un archivo, el programa continuaráborrando los otros que le fueron pasados.

- Me parece que a esta altura ya debes tener curiosidad por ver el programa que restaura el

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 57 / 123

Page 58: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

archivo borrado, no es así? Entonces, ahí va un desafio: hazlo en casa y me lo traes para discutirloen nuestro próximo encuentro aqui en el bar.

- Caramba, me parece que en ese voy a fracasar, no sé ni como comenzar...

- Amigo mio, este programa es como todo lo que se hace en Shell, extremamente fácil, es paraser hecho en no más de 10 líneas. No te olvides que el archivo borrado está grabado en/tmp/$LOGNAME y que su última línea es el directorio en que estaba antes de ser "borrado".Tampoco te olvides de comprobar si fue pasado el nombre del archivo a ser borrado.

- En fin, voy a tratar, pero no sé...

- Ten fé hombre, te estoy diciendo que es fácil! Cualquier duda me pasas un e-mail [email protected]. Ahora basta de conversación que ya estoy con la garganta seca de tantohablar. Me acompañas en el próximo "chopp" o vas a salir corriendo para hacer el script quepasé?

- Déjame pensar un poco...

- Mozo, trae otro "chopp" mientras él piensa!

Voy aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos quequien quiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 28 Nov 2006

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa006 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa006 58 / 123

Page 59: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa007Controles: - Última Atualização: [06 Feb 2008 - V.9]

Conversación de Bar Parte VII

El comando tputY ahora podemos leer los dados de la pantalla

Vamos a leer archivos?

- Como dijiste? Repite que no te entiendo! Se te derritieron los pensamientos para hacer elscriptiziño que te pedí?

- Si, realmente tuve que colocar mucha materia gris en la pantalla, pero creo que lo conseguí!Bueno, por lo menos en los tests que hice la cosa funcionó, pero tu siempre me colocas piedras en

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 59 / 123

Page 60: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

el camino!

- No sera tanto, programar en shell es muy fácil, lo que vale son los consejos y los detalles deprogramación que te doy, que no son triviales. Las correcciones que te hago, son justamente paramostrarlos. Pero vamos a pedir dos "chopps" y le echo una ojeada a tu script.

- Mozo, trae dos. No te olvides que uno es sin espuma!

$ cat restaura #!/bin/bash # # Restaura archivos borrados vía erreeme #

if [ $# -eq 0 ] then echo "Uso: $0 " exit 1 fi # Lee el nombre del directorio original en la última líneaDir=`tail -1 /tmp/$LOGNAME/$1` # O grep -v borra la última línea y crea el # archivo con directorio ynombres originales grep -v $Dir /tmp/$LOGNAME/$1 > $Dir/$1 # Borra el archivo que ya estabamoribundo rm /tmp/$LOGNAME/$1

- Un momento, déjame ver se lo entendí. Primero colocas en la variable Dir la última línea delarchivo cuyo nombre está formado por /tmp/nombre del operador ($LOGNAME)/parámetropasado con el nombre del archivo a ser restaurado ($1). Enseguida el grep -v que montaste borraesa línea en que estaba el nombre del directorio, o sea, siempre es la última y manda lo que restadel archivo, que sería el archivo ya limpio, hacia el directorio original para después borrar elarchivo del "cubo de la basura"; S E N S A C I O N A L! Impecable! Ningun error! Lo viste? ya leestás tomando las medidas al shell!

- Entonces vamos a continuar, basta ya de bla-bla-bla, de que vas a hablar hoy?

- Ah! estoy viendo que el bichito del Shell se te contagió. Que bueno, vamos a ver como sepueden (y deben) leer datos y formatear pantallas, pero primero vamos a conocer un comando quete da todas las herramientas para que formatees tu pantalla de entrada de datos.

El comando tputEste comando se usa principalmente para posicionar el cursor en la pantalla, sin embargo tambiénes muy usado para borrar datos de la pantalla, saber la cantidad de líneas y columnas de lapantalla, posicionar correctamente un campo, borrar un campo cuya entrada se detectó comoerror. En fin, casi toda la formatación de la pantalla es hecha por este comando.

Unos pocos atributos del comando tput pueden eventualmente no funcionar, esto en el caso deque el modelo de terminal definido por la variable $TERM, no tenga esta posibilidad incorporada.

En la tabla siguiente, se presentan los principales atributos del comando y los efectos ejecutadossobre la pantalla, pero debes saber que existen muchos más que esos, mira sino:

$ tput it 8

Este ejemplo devolvió el tamaño inicial del <TAB> ( Initial T ab), y dime una cosa: para que quierosaber eso? Si quieres saber todo sobre el comando tput (y mira que es de nunca acabar), vea a:http://www.cs.utah.edu/dept/old/texinfo/tput/tput.html#SEC4.

Principales Opciones del Comando tputOpciones

de tput Efecto

cup lincol

CUrsor Position - Posiciona el cursor en la línea lin y columna col. El origen es cero

bold Coloca la pantalla en modo de realcerev Coloca la pantalla en modo de vídeo inversosmso

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 60 / 123

Page 61: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

rc Restore Cursor position - Coloca el cursor en la posición marcada por el último sc

smso Idéntico al anteriorsmul A partir de esta instrucción, los caracteres tecleados aparecerán sublineados en la

pantallablink Los caracteres tecleados aparecerán intermitentessgr0 Después de usar uno de los atributos de arriba, se usa este para restaurar la

pantalla a su modo normalreset Limpia la pantalla y restaura sus definiciones de acuerdo con el terminfo o sea, la

pantalla vuelve al patrón definido por la variable $TERM lines Devuelve la cantidad de líneas de la pantalla en el momento de la instruccióncols Devuelve la cantidad de columnas de la pantalla en el momento de la instrucciónel Erase Line - Borra la línea a partir de la posición del cursored Erase Display - Borra la pantalla a partir de la posición del cursor

il n Insert Lines - Introduce n líneas a partir de la posición del cursordl n Delete Lines - Borra n líneas a partir de la posición del cursorech n Erase CHaracters - Borra n caracteres a partir de la posición del cursorsc Save Cursor position - Graba la posición del cursor

Vamos a hacer un programa bien sencillo para mostrar algunos atributos de este comando. Es elfamoso usado y abusado Hola Mundo, sólo que esta frase será escrita en el centro de la pantalla yen vídeo inverso y después de eso, el cursor volverá hasta la posición en que estaba antes deescribir esta frase tan creativa. Observa:

$ cat hola.sh #!/bin/bash # Script bobo para testar # el comando tput (versión 1)

Columnas=`tput cols` # Grabando cantidad columnas Líneas=`tput lines` # Grabando cantidadlíneas Línea=$((Líneas / 2)) # Cual es la línea del medio de la pantalla? Columna=$(((Columnas -11) / 2)) # Centrando el mensaje en la pantalla tput sc # Grabando posición del cursor tput cup$Línea $Columna # Posicionándose para escribir tput rev # Vídeo inverso echo Hola Mundo! tputsgr0 # Restaura el vídeo a normal tput rc # Restaura el cursor a la posición original

Como el programa ya está todo comentado, creo que la única explicación necesaria sería para lalínea en que es creada la variable Columna y lo extraño allí es aquél número 11, este numero es eltamaño de la cadena que pretendo escribir (Hola Mundo).

De esta forma, este programa solamente conseguiría centrar cadenas de 11 caracteres, sinembargo, mira esto:

$ var=Conversa $ echo ${#var} 8 $ var="Conversa de Bar" $ echo ${#var} 15

Ahhh, mejoró! Entonces ahora sabemos que la construcción ${#variable} devuelve la cantidad decaracteres de variable. De esta forma, vamos a optimizar nuestro programa para que escriba envídeo inverso y en el centro de la pantalla, la cadena pasada como parámetro y que después elcursor vuelva a la posición en que estaba antes de la ejecución del script.

$ cat hola.sh #!/bin/bash # Script bobo para testar # el comando tput (versión 2)

Columnas=`tput cols` # Grabando cantidad columnas Líneas=`tput lines` # Grabando cantidadlíneas Línea=$((Líneas / 2)) # Cual es la línea del medio de la pantalla? Columna=$(((Columnas -${#1}) / 2)) #Centrando el mensaje en la pantalla put sc # Grabando posición del cursor tput cup$Línea $Columna # Posicionándose para escribir tput rev # Vídeo inverso echo $1 tput sgr0 #Restaura vídeo a normal tput rc # Restaura cursor en la posición original

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 61 / 123

Page 62: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Este script es igual al anterior, sólo que cambiamos el valor fijo de la versión anterior (9), por${#1}, donde éste 1 es el $1 o sea, esta construcción devuelve el tamaño del primer parámetropasado para el programa. Si el parámetro que yo quisiese pasar tuviese espacios en blanco,tendría que colocarlo todo entre comillas, sino el $1 sería solamente el primer pedazo. Para evitareste problema, es solo necesario substituir el $1 por $*, que como sabemos es el conjunto detodos los parámetros. Entonces aquella línea quedaría así:

Columna=`$(((Columnas - ${#*}) / 2))` #Centrando el mensaje en la pantalla

y la línea echo $1 pasaría a ser echo $*. Pero no te olvides de que cuando lo ejecutes, tienes quepasar la frase que deseas centrar como un parámetro.

Y ahora podemos leer los dados de la pantallaBien, a partir de ahora vamos a aprender todo sobre lectura, solo que no te puedo enseñar a leerlas cartas o el futuro, porque sino ya seria rico, estaria en un pub de Londres, tomando scotch y noen un bar tomando "chopp". Pero vamos a continuar.

La última vez que nos encontramos aquí ya te dí una introducción sobre el comando read. Paracomenzar su análisis más detallada. fíjate en esto:

$ read var1 var2 var3 Conversa de Bar $ echo $var1 Conversa $ echo $var2 de $ echo $var3 Bar$ read var1 var2 Conversa de Bar $ echo $var1 Conversa $ echo $var2 de Bar

Como viste, el read recibe una lista separada por espacios en blanco y coloca cada ítem de estalista en una variable. Si la cantidad de variables es menor que la cantidad de ítems, la últimavariable recibe el resto de los parámetros.

Yo mencioné una lista separada por espacios en blanco? Pero ahora que ya lo conoces todosobre el $IFS (Inter Field Separator) que te presenté cuando hablamos del comando for, todavíacrees eso? Vamos a verificarlo directamente en el prompt:

$ oIFS="$IFS" $ IFS=: $ read var1 var2 var3 Conversa de Bar $ echo $var1 Conversa de Bar $echo $var2

$ echo $var3

$ read var1 var2 var3 Conversa:de:Bar $ echo $var1 Conversa $ echo $var2 de $ echo $var3 Bar$ IFS="$oIFS"

Te diste cuenta, estaba equivocado! La verdad es que el read lee una lista, así como el for,separada por los caracteres de la variable $IFS. Fíjate entonces como esto puede facilitarte lavida:

$ grep julio /etc/passwd julio:x:500:544:Julio C. Neves - 7070:/home/julio:/bin/bash $ oIFS="$IFS" #Grabando IFS $ IFS=: $ grep julio /etc/passwd | read lname lixo uid gid coment home shell $ echo -e "$lname\n$uid\n$gid\n$coment\n$home\n$shell" julio 500 544 Julio C. Neves - 7070 /home/julio/bin/bash $ IFS="$oIFS" # Restaurando IFS

Como viste, la salda del grep fue redireccionada hacia el comando read que leyó todos loscampos de una sola vez. La opción -e del echo fue usada para que el \n fuera entendido como unsalto de línea (new line), y no como un literal.

En el Bash existen diversas opciones del read que sirven para facilitarte la vida. Observa lasiguiente tabla:

Opciones del comando read en Bash

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 62 / 123

Page 63: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

-s Lo que está siendo tecleado no aparece en la pantalla

Opción Acción -p prompt Escribe el prompt antes de hacer la lectura

-n num Lee hasta num caracteres -t seg Espera seg segundos para que concluya la lectura

Y ahora directo a los ejemplos cortos para demostrar estas opciones.

Para leer un campo "Matrícula":

$ echo -n "Matricula: "; read Mat # -n no salta línea Matricula: 12345 $ echo $Mat 12345

O simplificando con la opción -p:

$ read -p "Matricula: " Mat Matricula: 12345 $ echo $Mat 12345

Para leer una determinada cantidad de caracteres:

$ read -n5 -p"CEP: " Num ; read -n3 -p- Compl CEP: 12345-678$ $ echo $Num 12345 $ echo$Compl 678

En este ejemplo hicimos dos read: uno para la primera parte del CEP y otra para su complementoy de este modo formateamos la entrada de datos. El signo de pesos ($) después del últimonúmero tecleado, es porque el read no tiene el new-line implicito por default como lo tiene el echo.

Para leer hasta que un determinado tiempo termine (conocido como time out):

$ read -t2 -p "Digite su nombre completo: " Nom || echo 'Ah perezoso!' Escriba su nombrecompleto: JAh perezoso! $ echo $Nom

$

Obviamente esto fue una broma, ya que solo tenía 3 segundos para escribir mi nombre completo ysólo me dio tiempo de teclear una J (aquella pegada al Ah), pero me sirvió para mostrar doscosas:

1. El comando después del par de barras verticales (||) (el o lógico, te acuerdas?) seráejecutado en el caso que la escritura no haya terminado en el tiempo estipulado;

2. La variable Nom permaneció vacía. Esta tendrá valores solamente cuando el <ENTER> seatecleado.

Para leer un dato sin ser mostrado en la pantalla:

$ read -sp "Seña: " Seña: $ echo $REPLY secreto :)

Aprovecho un error para mostrarte un detalle de programación. Cuando escribi la primera línea,me olvidé de colocar el nombre de la variable que iría a recibir la contraseña, y sólo noté esocuando fui a listar su valor. Por suerte la variable $REPLY del Bash, posee la última cadena leída yme aproveché de esto para no perder el viaje. Verifica tu mismo lo que acabo de hacer.

Pero el ejemplo que dí, era para mostrar que la opción -s impide que lo que está siendo tecleadose vea en la pantalla. Como en el ejemplo anterior, la falta del new-line hizo con que el prompt delcomando ($) permaneciese en la misma línea.

Bien, ahora que sabemos leer de la pantalla, veamos como se leen los datos de los archivos.

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 63 / 123

Page 64: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Vamos a leer archivos?

Como ya te habia dicho y te debes de acordar, el while verifica un comando y ejecuta un bloquede instrucciones mientras este comando de una respuesta correcta. Cuando estás leyendo unarchivo que te dá permiso de lectura, el read sólo dará una respuesta errónea cuando alcance elEOF (end of file), de esta forma podemos leer un archivo de dos maneras:

1 - Redireccionando la entrada del archivo hacia el bloque del while así:

while read Línea do echo $Línea done < archivo

2 - Redireccionando la salida de un cat hacia el while, de la siguiente forma:

cat archivo | while read Línea do echo $Línea done

Cada uno de los procesos tiene sus ventajas y desventajas:

Ventajas del primer proceso:

Es más rápido;No necesita de un subshell para asistirlo;

Desventaja del primer proceso:

en un bloque de instrucciones grande, el redireccionamento queda poco visible, lo que aveces perjudica la visualización del código;

Ventaja del segundo proceso:

Como el nombre del archivo está antes del while, es más fácil la visualización del código.

Desventajas del segundo proceso:

El Pipe (|) llama un subshell para interpretarlo, volviendo el proceso más lento, pesado y aveces problemático (mira los ejemplos que siguen).

$ cat readpipe.sh #!/bin/bash # readpipe.sh # Ejemplo de read pasando archivo por pipe.

Ultimo="(vacío)" cat $0 | # Pasando el arch. del script ($0) p/ while while read Línea doUltimo="$Línea" echo "-$Ultimo-" done echo "Acabó, Último=:$Ultimo:"

Vamos a ver su ejecución:

$ readpipe.sh -#!/bin/bash- -# readpipe.sh- -# Ejemplo de read pasando archivo por pipe.- -- -Ultimo="(vacío)"- -cat $0 | # Pasando el arch. del script ($0) p/ while- -while read Línea- -do- -Ultimo="$Línea"- -echo "-$Ultimo-"- -done- -echo "Acabó, Último=:$Ultimo:"- Acabó,Último=:(vacío):

Como viste, el script lista todas sus própias líneas con un signo de menos (-) antes y otro despuésy al final muestra el contenido de la variable $Ultimo. Sin embargo, observa que el contenido deesta variable permanece como (vacío).

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 64 / 123

Page 65: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Será que la variable no fue actualizada?

- Lo fue, y eso puede ser comprobado porque la línea echo "-$Ultimo-" lista correctamente laslíneas.

- Entonces que paso?

- Pues que como ya te dije, el bloque de instrucciones redireccionado por el pipe (|) esejecutado en un subshell y allí las variables son actualizadas. Cuando este subshell termina, lasactualizaciones de las variables se van junto con él, para los quintos infiernos. Observa que voy ahacer un pequeño cambio pasando el archivo por redireccionamento de entrada (<) y así las cosaspasarán a funcionar de una forma más perfecta:

$ cat redirread.sh #!/bin/bash # redirread.sh # Ejemplo de read pasando archivo porredireccionamento de entrada (<).

Ultimo="(vacío)" while read Línea do Ultimo="$Línea" echo "-$Ultimo-" done < $0 # Pasando elarch. del script ($0) p/ while echo "Acabó, Último=:$Ultimo:"

Y mira su ejecución sin errores:

$ redirread.sh -#!/bin/bash- -# redirread.sh- -# Ejemplo de read pasando archivo porredireccionamento de entrada (<).- -- -Ultimo="(vacío)"- -while read Línea- -do- -Ultimo="$Línea"- -echo "-$Ultimo-"- -done < $0 # Pasando el arch. del script ($0) p/ while- -echo "Acabó,Último=:$Ultimo:"- Acabó, Último=:echo "Acabó, Último=:$Ultimo:":

Bien amigos de la Red Shell, para finalizar el comando read sólo falta un pequeño e importantedetalle que voy a mostrar utilizando un ejemplo práctico. Imagina que quieres listar en pantalla unarchivo y que cada diez registros esta lista se detenga para que el operador pueda leer elcontenido de la pantalla y sólo volverá a funcionar (scroll) después que el operador pulse cualquiertecla. Para no gastar absurdamente papel (de la Linux Magazine), voy a hacer esta lista en lahorizontal y mi archivo (numeros), que tiene 30 registros solamente con números secuenciales.Mira:

$ seq 30 > numeros $ cat 10porpag.sh #!/bin/bash # Prg de test para escribir # 10 líneas y pararpara leer # Versión 1

while read Num do let ContLin++ # Contando... echo -n "$Num " # -n para no saltar línea ((ContLin% 10)) > /dev/null || read done < numeros

Como forma de hacer un programa genérico creamos la variable $ContLin (por que en la vida real,los registros no son solamente números secuenciales) y pararemos para leer cuando el resto de ladivisión por 10 sea cero (mandando la salida para /dev/null de forma de que no aparezca en lapantalla, ensuciandola). Sin embargo, cuando fui a ejecutarlo me dio el siguiente error:

$ 10porpag.sh 1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 18 19 20 21 23 24 25 26 27 28 29 30

Fíjate que falta el número 11 y que la lista no se paro en el read. Lo que paso es que la entrada delloop estaba redireccionada desde el archivo numeros y de esta forma, la lectura fue hecha encimade este archivo, así perdimos el 11 (y tambiém el 22).

Vamos a mostrar entonces como debería quedar para funcionar correctamente:

$ cat 10porpag.sh #!/bin/bash # Prg de test para escribir # 10 líneas y parar para leer # Versión 2

while read Num do let ContLin++ # Contando... echo -n "$Num " # -n para no saltar línea ((ContLin% 10)) > /dev/null || read < /dev/tty done < numeros

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 65 / 123

Page 66: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Observa que ahora la entrada del read fue redireccionada desde /dev/tty, que no es nada másque el terminal corriente, forzando de esta forma que la lectura sera hecha del teclado y no denúmeros. Es bueno resaltar que esto no sucede solamente cuando usamos el redireccionamentode entrada, se hubieramos usado el redireccionamento via pipe (|), habría pasado lo mismo.

Observa ahora su ejecución:

$ 10porpag.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

Esto está casi bien, pero falta un poco para quedar excelente. Vamos a mejorar un poco elejemplo para que lo reproduzcas y verifiques (pero antes de verificar, aumenta el número deregistros de numeros o reduce el tamaño de la pantalla, para que haya un salto de página).

$ cat 10porpag.sh #!/bin/bash # Prg de test para escribir # 10 líneas y parar para leer # Versión 3

clear while read Num do ((ContLin++)) # Contando... echo "$Num" ((ContLin % (`tput lines` - 3))) || {read -n1 -p"Teclee Algo " < /dev/tty # para leer cualquier caracter clear # limpia la pantalla despuesde la lectura } done < numeros

El cambio principal hecho en este ejemplo, es con relación al salto de página, ya que esta hechoen cada cantidad-de-líneas-de-pantalla (tput lines) menos (-) 3, o sea, si la pantalla tiene 25líneas, listará 22 registros y parará para su lectura. En el comando read también fue hecha unaalteración, incluyendo un -n1 para leer solamente un caracter sin ser necesariamente un <ENTER> yla opción -p para dar el mensaje.

- Bien amigo mio, por hoy ya basta porque me parece que estás saturado de esto...

- No, no lo estoy, realmente puede continuar...

- Si tu no lo estás, yo sí... Pero ya que estás tan entusiasmado con el Shell, te voy a dejar unejercicio de aprendizaje que mejorara tu CDteca y que es bastante simple. Reescribe tu programaque registra CDs para montar toda la pantalla con un único echo y que después vayaposicionandose frente a cada campo para recibir los valores que serán tecleados por el operador.

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar también para mandar mi aviso publicitario: puedes decirle a los amigos que quienquiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 17 Jan 2007

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa007 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa007 66 / 123

Page 67: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa008Controles: - Última Atualização: [06 Feb 2008 - V.9]

Conversación de bar Parte VIII

FuncionesEl comando source

- Hola amigo, como estás?

- Muy bien!, quería mostrarte lo que hice, pero ya sé que tu quieres ir rápido a lo que interesa,no?

- Solo para llevarte la contraria, hoy voy a dejar que me muestres tu "programita". Venga,

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 67 / 123

Page 68: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

muéstrame lo que hiciste.

- Ahhh ... el ejercicio que me pasaste es muy extenso. Yo lo resolví así:

$ cat musinc5 #!/bin/bash # Registra CDs (versión 5) # clear LineaMesg=$((`tput lines` - 3)) #Linea que define cuantos mensajes serán pasados de una vez a la pantalla TotCols=$(tput cols) #Cantidad de columnas de la pantalla para encuadrar mensajes echo " Inclusión de Músicas ========= == ======= Título del Álbum: | Este campo fue Pista: < creado solamente para |orientar como llenar Nombre de la Música: Intérprete:" # Pantalla montada con un único echowhile true do tput cup 5 38; tput el # Posiciona y limpia linea read Albun [ ! "$Albun" ] && #Operador pulso <ENTER> { Msg="Desea Terminar? (S/n)" TamMsg=${#Msg}Col=$(((TotCols - TamMsg) / 2)) # Centra mensaje en la linea tput cup $LineaMesg $Col echo"$Msg" tput cup $LineaMesg $((Col + TamMsg + 1)) read -n1 SN tput cup $LineaMesg $Col; tput el# Borra mensaje de la pantalla [ $SN = "N" -o $SN = "n" ] && continue # $SN es igual a N o (-o) n?clear; exit # Fin de la ejecución } grep "^$Albun\^" musicas > /dev/null && { Msg="Este álbum yaestá incluido" TamMsg=${#Msg} Col=$(((TotCols - TamMsg) / 2)) # Centra mensaje en la lineatput cup $LineaMesg $Col echo "$Msg" read -n1 tput cup $LineaMesg $Col; tput el # Borramensaje de la pantalla continue # Vuelve para leer otro álbum } Reg="$Albun^" # $Reg recibirá losdatos para grabación elArtista= # Variable que graba artista anterior while true do ((Track++)) tputcup 7 38 echo $Track tput cup 9 38 # Posiciona para leer música read Musica [ "$Musica" ] || # Siel operador escribio ... { Msg="Fin del Álbum? (S/n)" TamMsg=${#Msg} Col=$(((TotCols -TamMsg) / 2)) # Centra mensaje en la linea tput cup $LineaMesg $Col echo "$Msg" tput cup$LineaMesg $((Col + TamMsg + 1) read -n1 SN tput cup $LineaMesg $Col; tput el # Borramensaje de la pantalla [ "$SN" = N -o "$SN" = n ]&&continue # $SN es igual a N o (-o) n? break #Sale del loop para grabar } tput cup 11 38 # Posiciona para leer Artista [ "$elArtista" ]&& echo -n"($elArtista) " # Artista anterior es default read Artista [ "$Artista" ] && elArtista="$Artista"Reg="$Reg$elArtista~$Musica:" # Montando registro tput cup 9 38; tput el # Borra Música de lapantalla tput cup 11 38; tput el # Borra Artista de la pantalla done echo "$Reg" >> musicas # Grabaregistro en el fin del archivo sort musicas -0 musicas # Clasifica el archivo done

- Si, el programa esta bien, esta todo bien estructurado, pero me gustaría comentarte un poco loque hiciste:

Solo para recordarte, las siguientes construcciones: [ ! $Albun ] && y [ $Musica ] ||representan lo mismo, en el primer caso, comprobamos si la variable $Album no (!) tienenada dentro, entonces (&&) ... y en el segundo, comprobamos lo mismo en $Musica, si no (||)...Si te quejaste por el tamaño, es porque todavía no te pase algunos trucos. Fíjate que lamayor parte del script es para dar mensajes centrados en la penúltima linea de la pantalla.Fíjate también que algunos mensajes piden un S o un N y otros son sólo de advertencia.Sería el caso típico del uso de funciones, que serían escritas solamente una vez y llamadaspara ejecutar en diversos puntos del script . Voy a hacer dos funciones para resolver estoscasos y vamos a incorporarlas a tu programa para ver el resultado final.

Funciones - Mozo! Ahora tráeme dos "chops" bien helados, uno sin espuma, para que me de inspiración.

Pregunta () { # La función recibe 3 parámetros en el siguiente orden: # $1 - Mensaje que será mostrado en la pantalla # $2 - Valor que será aceptado como respuesta por defecto # $3 - Otro valor aceptado # Suponiendo que $1=Acepta?, $2=s y $3=n, la linea a # seguir colocaría en Msg el valor "Acepta? (S/n)" local Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)" local TamMsg=${#Msg}

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 68 / 123

Page 69: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

local Col=$(((TotCols - TamMsg) / 2)) # Centra mensaje en la linea tput cup $LineaMesg $Col echo "$Msg" tput cup $LineaMesg $((Col + TamMsg + 1)) read -n1 SN [ ! $SN ] && SN=$2 # Si esta vacía coloca por defecto en SN echo $SN | tr A-Z a-z # La salida de SN será en minúscula tput cup $LineaMesg $Col; tput el # Borra mensaje de la pantalla return # Sale de la función }

Como podemos ver, una función es definida cuando hacemos nombre_de_la_función () y todo sucuerpo esta entre llaves ({}). Así como charlamos aquí en el Bar sobre pasar parámetros, lasfunciones los reciben de la misma forma, o sea, son parámetros de posición ($1, $2, ..., $n) ytodas las reglas que se aplican al pase de parámetros para programas, también valen parafunciones, pero es muy importante aclarar que los parámetros pasados hacia un programa no semezclan con aquellos que éste pasó hacia sus funciones. Esto significa, por ejemplo, que el $1 deun script es diferente del $1 de una de sus funciones.

Fíjate que las variables $Msg, $TamMsg y $Col son de uso restringido de esta rutina, y por eso fueroncreadas como local. La finalidad de esto es simplemente economizar memoria, ya que al salir dela rutina, todas serán destruidas y si no hubiese usado esta opción, se quedarían residentes en lamemoria.

La linea de código que crea local Msg, junta el texto recibido ($1) abre paréntesis, la respuestadefault ($2) en mayúscula, una barra, la otra respuesta ($3) en minúscula y finaliza cerrando elparéntesis. Uso esta forma para, que al mismo tiempo, pueda mostrar las opciones disponibles ydestacar la respuesta ofrecida como default.

Casi al final de la rutina, la respuesta recibida ($SN) se pasa a minúscula de forma que en el cuerpodel programa no se necesite hacer esta prueba.

Veamos ahora como quedaría la función para presentar un mensaje en la pantalla:

function MandaMsg { # La función recibe solamente un parámetro # con el mensaje que se desea mostrar, # para no obligar al programador que pase # el mensaje entre comillas, usaremos $* (todos # los parámetros, te acuerdas?) y no $1. local Msg="$*" local TamMsg=${#Msg} local Col=$(((TotCols - TamMsg) / 2)) # Centra el mensaje en la linea tput cup $LineaMesg $Col echo "$Msg" read -n1 tput cup $LineaMesg $Col; tput el # Borra el mensaje de la pantalla return # Sale de la función }

Esta es otra forma de definir una función: no la llamamos como en el ejemplo anterior usando unaconstrucción con la sintaxis nombre_de_la_función (), sino como functionnombre_de_la_función. No tiene ninguna diferencia con la anterior, excepto que, como consta enlos comentarios, usamos la variable $* que como ya sabemos es el conjunto de todos losparámetros pasados, para que el programador no necesite usar comillas envolviendo el mensajeque desea pasar para la función.

Para terminar con este blá-blá-blá vamos a ver entonces las alteraciones que el programa necesitacuando usamos el concepto de funciones:

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 69 / 123

Page 70: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ cat musinc6 #!/bin/bash # Registra CDs (versión 6) #

# Área de las variables globales LineaMesg=$((`tput lines` - 3)) # Linea que mensajes serán dadospara el operador TotCols=$(tput cols) # Cantidad de columnas de la pantalla para encuadrarmensajes

# Área de las funciones Pregunta () { # La función recibe 3 parámetros en el siguiente orden: # $1 -Mensaje que será mostrado en la pantalla # $2 - Valor que será aceptado como respuesta default# $3 - Otro valor aceptado # Suponiendo que $1=Acepta?, $2=s y $3=n, la linea a # seguircolocaría en Msg el valor "Acepta? (S/n)" local Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)" local TamMsg=${#Msg} local Col=$(((TotCols - TamMsg) / 2)) # Centra mensaje en la linea tputcup $LineaMesg $Col echo "$Msg" tput cup $LineaMesg $((Col + TamMsg + 1)) read -n1 SN [ !$SN ] && SN=$2 # Si vacia coloca default en SN echo $SN | tr A-Z a-z # La salida de SN será enminúscula tput cup $LineaMesg $Col; tput el # Borra mensaje de la pantalla return # Sale de lafunción } function MandaMsg { # La función recibe solamente un parametro # con el mensaje quese desea mostrar, # para no obligar al programador que pase # el mensaje entre comillas,usaremos $* (todos # los parametros, te acuerdas?) y no $1. local Msg="$*" localTamMsg=${#Msg} local Col=$(((TotCols - TamMsg) / 2)) # Centra mensaje en la linea tput cup$LineaMesg $Col echo "$Msg" read -n1 tput cup $LineaMesg $Col; tput el # Borra mensaje de lapantalla return # Sale de la función }

# El cuerpo del programa propiamente dicho comienza aqui clear echo " Inclusión de Músicas ========= == ======= Título del Álbun: | Este campo fue Pista: < creado solamente para |orientar como llenar Nombre de la Música: Intérprete:" # Pantalla montada con un único echowhile true do tput cup 5 38; tput el # Posiciona y limpia linea read Albun [ ! "$Albun" ] && #Operador dió { Pregunta "Desea Terminar" s n [ $SN = "n" ] && continue # Ahora sólo verificominúsculas clear; exit # Fin de la ejecución } grep -iq "^$Albun\^" musicas 2> /dev/null && { MandaMsg Este álbun ya esta catastrado continue # Vuelve para leer otro álbun } Reg="$Albun^" #$Reg recibirá los datos de grabación elArtista= # Grabará artista anterior while true do ((Track++))tput cup 7 38 echo $Track tput cup 9 38 # Posiciona para leer música read Musica [ "$Musica" ] || #Si el operador dio ... { Pregunta "Fin de Álbun?" s n [ "$SN" = n ] && continue # Ahora solo pruebala minuscula break # Sale del loop para grabar datos } tput cup 11 38 # Posiciona para leer Artista[ "$elArtista" ]&& echo -n "($elArtista) " # Artista anterior es default read Artista [ "$Artista" ] &&elArtista="$Artista" Reg="$Reg$elArtista~$Musica:" # Montando registro tput cup 9 38; tput el #Borra Musica de la pantalla tput cup 11 38; tput el # Borra Artista de la pantalla done echo "$Reg">> musicas # Graba registro en el fin del archivo sort musicas -o musicas # Clasifica el archivodone

Fijate que la estructura del _script_esta como en el gráfico de abajo:

Cuerpo del Programa

Variables GlobalesFunciones

Esta estructuración es debida a que el Shell es un lenguaje interpretado y así el programa es leídode izquierda a derecha y de arriba para abajo. De esa forma, para que una variable sea vistasimultáneamente por el script y sus funciones, debe ser declarada (o inicializada) antes decualquier otra cosa. Las funciones deben ser declaradas antes del cuerpo del programapropiamente dicho para que en el lugar en que el programador mencione su nombre, el interpreteShell ya lo haya localizado antes y registrado que es una función.

Una cosa muy útil en el uso de funciones es tratar de hacerlas lo más generales posible, de formaque sirvan para otras aplicaciones, sin necesidad de tener que reescribirlas. Esas dos queacabamos de ver tienen uso general, pues es dificil hallar un script que tenga una entrada de datospor teclado que no use una rutina del tipo de la MandaMsg o no interaccione con el operador através de algo parecido a Pregunta.

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 70 / 123

Page 71: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Consejo de amigo: crea un archivo y cada función nueva que programes, añádela a este archivo.Así con el tiempo tendrás una bella biblioteca de funciones que te ahorrará mucho tiempo deprogramación.

El comando sourceFíjate si notas algo diferente en la salida del ls siguiente:

$ ls -la .bash_profile -rw-r--r-- 1 Julio unknown 4511 Mar 18 17:45 .bash_profile

No mires la respuesta y vuelve a prestar atención! De acuerdo, ya que no tienes paciencia parapensar y prefieres leer la respuesta, te voy a dar una pista: me parece que ya sabes que el.bash_profile es uno de los programas que son automáticamente "ejecutados" cuando tu te logeas(ARRGGHH! Odio este término). Ahora que te dí esta ayuda, mira nuevamente la salida del ls ydime que hay de diferente en ella.

Como te dije el .bash_profile es "ejecutado" en el momento del logon y fíjate que no tiene ningúnaprerrogativa de ejecución. Esto ocurre porque si tu lo ejecutaras como cualquier otro script simple,cuando terminara su ejecución, todo el ambiente generado por él moriría junto con el Shell en elcual fue ejecutado (te acuerdas que todos los scripts son ejecutados en subshells, verdad?).

Pues bien, es para cosas así que existe el comando source, también conocido por . (punto). Estecomando hace que no sea creado un nuevo Shell (un subshell) para ejecutar el programa que lees pasado como parámetro.

Mejor un ejemplo que 10.000 palabras. Mira el scriptiziño siguiente:

$ cat script_bobo cd .. ls

Simplemente debería ir hacia el directório superior del directório actual. Vamos a ejecutar unoscomandos que incluyen el script_bobo y vamos a analizar los resultados:

$ pwd /home/jneves $ script_bobo jneves juliana paula silvie $ pwd /home/jneves

Si yo mandé subir un directório, porque no subió? Subió sí! El subshell que fue creado paraejecutar el script subió y listó los directórios de los cuatro usuarios debajo del /home, solo que asíque el script acabó, el subshell se fue al limbo y con él, todo el ambiente creado. Mira ahora comola cosa cambia:

$ source script_bobo jneves juliana paula silvie $ pwd /home $ cd - /home/jneves $ . script_bobojneves juliana paula silvie $ pwd /home

Ahh! Ahora sí! Siendo pasado como parámetro del comando source o . (punto), el script fueejecutado en el Shell corriente dejando en este, todo el ambiente creado. Ahora damos un rewindhacia el inicio de la explicación sobre este comando. Un poco antes, hablamos del .bash_profile, ya estas alturas ya debes saber que su tarea es, inmediatamente después del login, dejar elambiente de trabajo preparado para el usuário, y ahora entendemos que por eso es ejecutadousando esta construcción.

Y ahora debes estarte preguntando, sólo sirve para eso este comando?, y yo te digo que sí, peroeso nos trae una cantidad de ventajas y una de las más usadas es tratar funciones como rutinasexternas. Mira una forma diferente de hacer nuestro programa para incluir CDs en el archivomusicas:

$ cat musinc7 #!/bin/bash # Registra CDs (versión7) #

# Área de varibles globales LinhaMesg=$((`tput lines` - 3)) # Línea que msgs serán dadas para

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 71 / 123

Page 72: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

operador TotCols=$(tput cols) # Qtd colunas de la tela para encuadrar msgs

# El cuerpo del programa propriamente dicho comienza aqui clear echo " Inclusión de Músicas ======== == ======= Título do Álbum: | Este campo fue Pista: < creado solamente para |orientar como llenar Nombre de la Música: Intérprete:" # Pantalla montada con un único echowhile true do tput cup 5 38; tput el # Posiciona y limpa línea read Album [ ! "$Album" ] && #Operador dió { source pergunta.func "Desea Terminar" s n [ $SN = "n" ] && continue # Ahora sóloverifico minúsculas clear; exit # Fin de la ejecución } grep -iq "^$Album\^" musicas 2> /dev/null && {. mandamsg.func Este álbum ya está catastrado continue # Vuelve para leer otro álbum }Reg="$Album^" # $Reg reciberá los datos de grabación oArtista= # Guardará artista anterior whiletrue do ((Faixa++)) tput cup 7 38 echo $Faixa tput cup 9 38 # Posiciona para leer música readMusica [ "$Musica" ] || # Si el operador hubiese dado ... { . pergunta.func "Fin del Álbum?" s n ["$SN" = n ] && continue # Ahora sólo verifico minúsculas break # Sale del loop para grabar datos }tput cup 11 38 # Posiciona para leer Artista [ "$oArtista" ] && echo -n "($oArtista) " # Artista anter.é default read Artista [ "$Artista" ] && oArtista="$Artista" Reg="$Reg$oArtista~$Musica:" #Montando registro tput cup 9 38; tput el # Borra Musica de la pantalla tput cup 11 38; tput el # BorraArtista de la pantalla done echo "$Reg" >> musicas # Graba registro en el fin del archivo sortmusicas -o musicas # Clasifica el archivo done

Ahora el programa disminuyo considerablemente de tamaño y las funciones fueron cambiadas porarchivos externos llamados pergunta.func y mandamsg.func, que de esta forma, pueden serllamados por cualquer otro programa y con eso, reutilizando su código.

Por motivos meramente didácticos las ejecuciones de pergunta.func y mandamsg.func estánsiendo llamadas por source y por . (punto) indiscriminadamente, sin embargo, prefiero el sourcepor ser más visible, lo que le da mayor legibilidad al código y facilita su manutenciónposteriormente.

Mira ahora como quedaron estos dos archivos:

$ cat pergunta.func # La función recibe 3 parámetros en el siguiente orden: # $1 - Mensaje a serenviado a la pantalla # $2 - Valor que sera aceptado como respuesta por defecto # $3 - El otrovalor aceptado # Suponiendo que $1=Acepta?, $2=s y $3=n, en la línea # de abajo colocaría enMsg el valor "Acepta? (s/n)" Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)"TamMsg=${#Msg} Col=$(((TotCols - TamMsg) / 2)) # Centra msg en la línea tput cup $LinhaMesg$Col echo "$Msg" tput cup $LinhaMesg $((Col + TamMsg + 1)) read -n1 SN [ ! $SN ] && SN=$2 #Si esta vacía coloca default en SN echo $SN | tr A-Z a-z # La salida de SN será en minúscula tputcup $LinhaMesg $Col; tput el # Borra msg de la pantalla $ cat mandamsg.func # La función recibesolamente un parámetro # con el mensaje que se desea exhibir, # para no obligar al programadora pasar # el msg entre comillas, usaremos $* (todos # los parámetro, recuerdas?) y no $1.Msg="$*" TamMsg=${#Msg} Col=$(((TotCols - TamMsg) / 2)) # Centra msg en la línea tput cup$LinhaMesg $Col echo "$Msg" read -n1 tput cup $LinhaMesg $Col; tput el # Borra msg de lapantalla

En ambos archivos, hice solamente dos cambios que veremos en las observaciones que siguen,sin embargo tengo tres observaciones más para hacer:

1. Las variables no están siendo declaradas como local, porque está es una directiva quesolamente puede ser usada en el cuerpo de funciones y por consiguiente, estas variablespermanecen en el ambiente del Shell, llenándolo de basura;

2. El comando return no está presente pero podría estarlo, sin alterar en nada la lógica, ya quesólo serviría para indicar un eventual error vía un código de retorno previamente establecido(por ejemplo return 1, return 2, ...), siendo que el return y return 0 son idénticos ysignifican rutina ejecutada sin errores;

3. El comando que estamos acostumbrados a usar para generar código de retorno es el exit,pero la salida de una rutina externa no puede ser hecha de esta forma, porque por estarsiendo ejecutada en el mismo Shell que el script llamador, el exit simplemente cerraría este

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 72 / 123

Page 73: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Shell, terminando la ejecución de todo el script;4. De donde surgió la variable LinhaMesg? Ella vino del musinc7, porque habia sido declarada

antes de la llamada de las rutinas (sin olvidar que el Shell que está interpretando el script yestas rutinas, es el mismo para todos);

5. Si decidiste usar rutinas externas, no seas haragán, abunda en los comentarios(principalmente sobre el pasaje de los parámetros) para facilitar la manutención y su usopara otros programas en el futuro.

- Bien, ahora ya tienes una cantidad de novedades para mejorar los scripts que hicimos. Teacuerdas del programa listartista en el cual pasabas el nombre de un artista como parámetro yél devolvia sus músicas? Era así:

$ cat listartista #!/bin/bash # Dado un artista, muestra sus músicas # versión 2

if [ $# -eq 0 ] then echo Usted debería haber pasado al menos un parámetro exit 1 fi

IFS=" :" for ArtMus in $(cut -f2 -d^ musicas) do echo "$ArtMus" | grep -i "^$*~" > /dev/null && echo$ArtMus | cut -f2 -d~ done

- Claro que me acuerdo!...

- Entonces para afirmar los conceptos que te pasé, hazlo con la pantalla formateada, en loop,de forma que solamente termine cuando reciba un <ENTER> puro en el nombre del artista. Ahhh!Cuando la lista llegue a la antepenúltima línea de la pantalla, el programa deberá detenerse paraque el operador pueda leerlas, o sea, imagina que la pantalla tenga 25 lineas. Cada 22 músicaslistadas (cantidad de líneas menos 3) el programa aguardará a que el operador teclee algo paraentonces continuar. Eventuales mensajes de error deben ser pasados usando la rutinamandamsg.func que acabamos de hacer.

- Mozo, trae dos más, el mio con poca presión...

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos quequien quiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 10 Jan 2007

-- PatricioReich - 24 Nov 2006

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa008 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa008 73 / 123

Page 74: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa009Controles: - Última Atualização: [14 Feb 2012 - V.10]

Conversación de Bar Parte IX

Mejorando la escrituraPrincipales Variables del ShellExpansión de parámetros

- Está bien, ya sé que vas a querer un "chopp" antes de empezar, pero tengo muchas ganas deenseñarte primero lo que hice, así que voy pidiéndote ya la bebida y enseguida te lo muestro.

- Mozo!, trae dos. El de él sin espuma para no ensuciarse el bigote...

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 74 / 123

Page 75: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Mientras el "chopp" no llega, déjame recordarte que me pediste que rehiciera el listartistacon la pantalla formateada, en loop, de forma que solamente termine cuando reciba un <ENTER>puro en el nombre del artista. Eventuales mensajes de error y preguntas deberían ser mostradasen la antepenúltima línea de la pantalla utilizando las rutina mandamsj.func y pregunta.func queacabamos de desarrollar.

- Primero optimize el mandamsj.func y el pregunta.func, que quedaron así:

$ cat mandamsj.func # La función recibe solamente un parámetro # con el mensaje que se deseaexhibir. # Para no obligar al programador a pasar # el msj entre comillas, usaremos $* (todos # losparámetros, recuerdas?) y no $1. Msj="$*" TamMsj=${#Msj} Col=$(((TotCols - TamMsj) / 2)) #Centra msj en la línea tput cup $líneaMesj $Col read -n1 -p "$Msj "

$ cat pregunta.func # La función recibe 3 parámetros en el siguiente orden: # $1 - mensaje a serdado en pantalla # $2 - Valor a ser acepto como respuesta default # $3 - El 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)" Msj="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)" TamMsj=${#Msj} Col=$(((TotCols -TamMsj) / 2)) # Centra msj en la línea tput cup $líneaMesj $Col read -n1 -p "$Msj " SN [ ! $SN ] &&SN=$2 # Si vacía coloca default en SN SN=$(echo $SN | tr A-Z a-z) # La salida de SN será enminúscula tput cup $líneaMesj $Col; tput el # Borra msj de la pantalla

- Y aquí va el grandullón ahora:

$ cat listartista3 #!/bin/bash # Dado un artista, muestra sus músicas # versión 3

líneaMesj=$((`tput lines` - 3)) # línea que msjs serán dados al operador TotCols=$(tput cols) # Ctdde columnas de la pantalla para encuadre de msjs

clear echo " +----------------------------------------------------+ | Lista Todas las Músicas de un DeterminadoArtista | | ----- ----- -- ------- -- -- ----------- ------- | | | | Informe el Artista: | +----------------------------------------------------+" while true do tput cup 5 51; tput ech 31 # ech=Erase chars (31 caracteres para noborrar barra vertical) read Nombre if [ ! "$Nombre" ] # $Nombre está vacío? then . pregunta.func"Desea Salir?" s n [ $SN = n ] && continue break fi

fgrep -iq "^$Nombre~" musicas || # fgrep no interpreta ^ como expresión regular { . mandamsjg.func"No existe música de este artista" continue }

tput cup 7 29; echo '| |' LinActual=8 IFS=" :" for ArtMus in $(cut -f2 -d^ musicas) # Excluye nombredel album do if echo "$ArtMus" | grep -iq "^$Nombre~" then tput cup $LinActual 29 echo -n '| ' echo$ArtMus | cut -f2 -d~ tput cup $LinActual 82 echo '|' let LinActual++ if [ $LinActual -eq $líneaMesj ]then . mandamsj.func "Teclee Algo para Continuar..." tput cup 7 0; tput ed # Borra la pantalla apartir de la línea 7 tput cup 7 29; echo '| |' LinActual=8 fi fi done tput cup $LinActual 29; echo '| |' tputcup $((++LinActual)) 29 read -n1 -p "+-----------Teclee Algo para Nueva Consulta----------+" tput cup 70; tput ed # Borra la pantalla a partir de la línea 7 done

- Caramba!, hoy llegaste con mucha fuerza! Pero me gustó la forma en que resolviste elproblema y estructuraste el programa. Fue más trabajoso pero la presentación quedó excelente yusaste bastante las opciones del tput. Vamos a comprobar el resultado con un álbum deEmerson, Lake & Palmer que tengo registrado:

+----------------------------------------------------+ | Lista Todas las Músicas de un Determinado Artista | | ----- ----- -- ------- -- -- ----------- ------- | | | | Informe el Artista: Emerson, Lake & Palmer | +----------------------------------------------------+ | | | Jerusalem | | Toccata |

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 75 / 123

Page 76: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

| Still ... You Turn Me On | | Benny The Bouncer | | Karn Evil 9 | | | +-----------Teclee Algo para Nueva Consulta----------+

Mejorando la escritura - Ufa! Ahora ya lo sabes todo sobre lectura, pero sobre escritura apenas estás gateando. Ya séque me vas a preguntar: - Pero, no era con el comando echo y con los redireccionamentos de salida que se escribe?

Si, con estos comandos escribes el 90% de las cosas necesarias, sin embargo, si necesitasescribir algo formateado te dará mucho trabajo. Para formatear la salida veremos ahora unainstrucción muy interesante - el printf - su sintaxis es la siguiente:

printf formato [argumento...]

En donde:formato - es una cadena de caracteres que contiene 3 tipos de objetos:

1. caracteres simples;2. caracteres para especificación de formato;3. secuencia de escape en el patrón del lenguaje C.

Argumento - es la cadena a ser impresa con el control del formato.

Cada uno de los caracteres utilizados para especificación de formato está precedido por elcarácter % y luego viene la especificación de formato de acuerdo con la tabla:

Tabla de los Caracteres de Formatación del printf

% Imprime un %. no existe ninguna conversión

Letra La expresión será impresa como:c Simple caracter d Número en sistema decimal e Notación científica exponencial f Número con punto decimal (float) g El menor entre los formatos %e y %f con supresión de los ceros no significativoso Número en sistema octal s Cadena de caracteres x Número en sistema hexadecimal

Las secuencias de escape patrón del lenguaje C son siempre precedidas por una barra invertida(\) y las reconocidas por el comando printf son:

Secuencias de Escape del printf

Secuencia Efecto a Suena el bip b Vuelve una posición (backspace) f

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 76 / 123

Page 77: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

t Avanza para la próxima marca de tabulación

f Salta para la próxima página lógica (form feed) n Salta para el inicio de la línea siguiente (line feed) r Vuelve para el inicio de la línea actual (carriage return)

Y no se acaba aquí, todavía hay más! Hay muchas más cosas sobre esta instrucción, pero comoson muchos detalles es por consiguiente aburrido de explicar y todavía peor de leer o estudiar, asíque vamos directos a los ejemplos con sus comentarios, que no estoy aqui para aburrir a nadie.

$ printf "%c" "1 caracter" 1$ Error! Sólo listó 1 caracter y no saltó de línea al final $ printf "%c\n" "1caracter" 1 Saltó de línea pero todavia no listó la cadena entera $ printf "%c caracteres\n" 1 1caracter Esta es la forma correcta, el %c recebió el 1 $ a=2 $ printf "%c caracteres\n" $a 2caracteres O %c recibió el valor de la variable $a $ printf "%10c caracteres\n" $a 2caracteres $ printf "%10c\n" $a caracteres 2 c

Observa que en los dos últimos ejemplos, en virtud del %c, sólo se listo un caracter de cadacadena. El 10 delante de la c, no significa 10 caracteres. Un número después del signo deporcentaje (%) significa el tamaño que la cadena tendrá depués de la ejecución del comando.

Y aqui vá un ejemplo:

$ printf "%d\n" 32 32 $ printf "%10d\n" 32 32 Rellena con blancos a la izquierda y con ceros $ printf"%04d\n" 32 0032 04 despues % significa 4 dígitos con ceros a la izquierda $ printf "%e\n" $(echo"scale=2 ; 100/6" | bc) 1.666000e+01 El default del %e es 6 decimales $ printf "%.2e\n" `echo"scale=2 ; 100/6" | bc` 1.67e+01 El .2 especificó dos decimales $ printf "%f\n" 32.3 32.300000 Eldefault del %f es 6 decimales $ printf "%.2f\n" 32.3 32.30 El .2 especificó dos decimales $ printf"%.3f\n" `echo "scale=2 ; 100/6" | bc` 33.330 El bc devolvió 2 decimales. El printf colocó 0 a laderecha $ printf "%o\n" 10 12 Convirtió el 10 en octal $ printf "%03o\n" 27 033 Así la conversiónqueda con más apariencia de octal, sí? $ printf "%s\n" Palabra Palabra $ printf "%15s\n" PalabraPalabra Palabra con 15 caracteres rellenados con blancos $ printf "%-15sNeves\n" PalabraPalabra Neves El menos (-) rellenó a la derecha con blancos $ printf "%.3s\n" Palabra Pal 3 Cortay deja sólo las 3 primeras $ printf "%10.3sa\n" Peteleca Peta Pet con 10 caracteres concatenadocon a (después del s) $ printf "EJEMPLO %x\n" 45232 EJEMPLO b0b0 Transformó en hexa perolos zeros no combinan $ printf "EJEMPLO %X\n" 45232 EJEMPLO B0B0? Así quedó mejor (Fíjateen la X mayúscula) $ printf "%X %XL%X\n" 49354 192 10 C0CA? C0LA?

El último ejemplo no es marketing y es bastante completo, voy a comentarlo paso a paso:

1. El primer %X convirtió 49354 en hexadecimal resultando C0CA (léase "ce", "cero", "ce" y "a");2. En seguida viene un espacio en blanco seguido por otro %XL. El %X convirtió el 192 dando

como resultado C0 que con el L hizo C0L;3. Y finalmente el último %X transformó el 10 en A.

Como puedes notar, la instrucción printf es bastante completa y compleja (por suerte el echo loresuelve casi todo).

Creo que cuando me decidí a explicar el printf a través de ejemplos, acerté plenamente, porqueno sabria como enumerar tantas reglitas sin hacer la lectura aburrida.

Principales Variables del ShellEl Bash posee diversas variables que sirven para dar informaciones sobre el ambiente o alterarlo.Su número es muy grande y no pretendo mostrártelas todas sino una pequeña parte, y que puedenayudarte en la elaboración de scripts. Ahí van las principales:

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 77 / 123

Page 78: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Principales variables del Bash

Variável Conteúdo

CDPATH Contiene los caminos que serán recorridos para intentar localizar undirectório especificado. A pesar de ser esta variable poco conocida, suuso debe ser incentivado por que nos ahorra mucho trabajo,principalmente en instalaciones con estructuras de directórios conbastante niveles.

HISTSIZE Limita el número de instrucciones que caben dentro del archivo históricode comandos (normalmente .bash_history pero efectivamente es lo queestá almacenado en la variable $HISTFILE). Su valor default es 500.

HOSTNAME El nombre del host actual (que también puede ser obtenido con elcomando uname -n).

LANG Usada para determinar el idioma hablado en el país (másespecificamente categoria de locale).

LINENO El número de la línea del script o de la función que está siendo ejecutada,su uso principal es para dar mensajes de error juntamente con lasvariables $0 (nombre del programa) y $FUNCNAME (nombre de la función enejecución)

LOGNAME Almacena el nombre de login del usuário.

MAILCHECK Especifica, en segundos, la frecuencia con que el Shell verificará lapresencia de correspondencia en los archivos indicados por lasvariables $MAILPATH o $MAIL. El tiempo patrón es de 60 segundos. Unavez que este tiempo expira, el Shell hará esta verificación antes deexhibir el próximo prompt primario (definido en $PS1). Si esta variableestuviera sin valor o con un valor menor o igual a cero, la verificación denueva correspondencia no será efectuada.

PATH Caminos que serán recorridos para intentar localizar un archivoespecificado. Como cada script es un archivo, en el caso de que uses eldirectorio actual (.) en su variable $PATH, no necesitarás usar el ./scrppara que scrp sea ejecutado. Basta hacer scrp. Este es el modo en queprocedo aqui en el Bar.

PIPESTATUS Es una variable del tipo vector (array) que contiene una lista de valoresde código de retorno del último pipeline ejecutado, o sea, un array queabriga cada uno de los $? de cada instrucción del último pipeline.

PROMPT_COMMAND Si esta variable recibe una instrucción, cada vez que tu des un <ENTER>directo en el prompt principal ($PS1), este comando será ejecutado. Esútil cuando se está repitiendo mucho una determinada instrucción.

PS1 Es el prompt principal. En "Conversa de Bar" usamos sus defaults: $para el usuário común y # para el root, pero es muy frecuente que estépersonalizado. Una curiosidad es que existen hasta concursos de quienprograma el $PS1 más creativo. (clique para dar una googlada)

PS2 También llamado prompt de continuación, es aquél signo de mayor (>)que aparece después de un <ENTER> sin que el comando haya sidofinalizado.

PWD Posee el camino completo ($PATH) del directório actual. Tiene el mismoefecto que el comando pwd.

RANDOM Cada vez que esta variable es llamada, devuelve un número entero, quees un número randómico entre 0 y 32767.

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 78 / 123

Page 79: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

TMOUT Si tuviera un valor mayor que cero, este valor será tomado como el patrónde timeout del comando read. En el prompt, este valor es interpretadocomo el tiempo de espera a una acción antes de finalizar la sesión.Suponiendo que la variable contenga 30, el Shell dará logout 30segundos después que el prompt esté sin ninguna acción.

REPLY Usa esta variable para recuperar el último campo leído, en caso de queno tenga ninguna variable asociada.

SECONDS Esta variable contiene la cantidad de segundos en que el Shell actualestá en uso. Úsala solamente para mostrar a un usuario aquello quellaman de sistema operacional, pero necesita de frecuentes boots.

CDPATH

$ echo $CDPATH .:..:~:/usr/local $ pwd /home/jneves/LM $ cd bin $ pwd /usr/local/bin

Como /usr/local estaba en mi variable $CDPATH, y no existía el directório bin en ninguno de susantecesores (., .. e ~), el cd fue ejecutado para /usr/local/bin

LANG

$ date Thu Apr 14 11:54:13 BRT 2005 $ LANG=pt_BR date Qui Abr 14 11:55:14 BRT 2005

Con la especificación de la variable LANG=pt_BR (portugués de Brasil), la fecha pasó a serformateada en el patrón brasileño. Es interesante observar que no se uso punto y coma (;) paraseparar la atribución de LANG del comando date.

PIPESTATUS

$ who jneves pts/0 Apr 11 16:26 (10.2.4.144) jneves pts/1 Apr 12 12:04 (10.2.4.144) $ who | grep^botelho $ echo ${PIPESTATUS[*]} 0 1

En este ejemplo mostramos que el usuário botelho no estaba "logado", en seguida ejecutamos unpipeline que lo filtraba. Se usa la notación [*] en un array para listar todos sus elementos, y deesta forma vimos que la primera instrucción (who) fue bien ejecutada (código de retorno 0) y lasiguiente (grep), no (código de retorno 1).

RANDOM

Para generar randómicamente un entero entre 0 y 100, hacemos:

$ echo $((RANDOM%101)) 73

O sea, tomamos el resto de la división por 101 del número randómico generado, porque el resto dela división de cualquier número por 101 varía entre 0 y 100.

REPLY

$ read -p "Digite S o N: " Digite S o N: N $ echo $REPLY N

Yo soy de la época en que la memoria era un bien precioso que costaba muuuuy caro. Entoncespara tomar un S o un N, no acostumbro guardar un espacio especial y por lo tanto, tomo de lavariable $REPLY lo que se escribio.

Expansión de parámetros

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 79 / 123

Page 80: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Bien, mucho de lo que vimos hasta ahora son comandos externos al Shell. Estos son de granayuda, facilitan la visualización, manutención y depuración del código, pero no son tan eficientescomo los intrínsecos (built-ins). Cuando nuestro problema sea prestaciones, debemos darpreferencia al uso de los intrínsecos y a partir de ahora te voy a mostrar algunas técnicas para quetu programa pise el acelerador.

En la tabla y ejemplos siguientes, veremos una serie de construcciones llamadas expansión (osubstitución) de parámetros (Parameter Expansion), que substituyen instrucciones como el cut, elexpr, el tr, el sed y otras, de forma más ágil.

Expansión de parámetros

${cadena/%subcad1/subcad2}

Si subcad1 s igual al fin de $cadena, entonces es cambiada porsubcad2

Expresión Resultado esperado

${var:-padrón} Si var no tiene valor, el resultado de la expresión es padrón

${#cadena} Tamaño de $cadena

${cadena:posición} Extrae una sub-cadena de $cadena a partir de posición. Origencero

${cadena:posición:tamaño} Extrae una sub-cadena de $cadena a partir de posición contamaño igual a tamaño. Origen cero

${cadena#expr} Corta la menor ocurrencia de $cadena a la izquierda de laexpresión expr

${cadena##expr} Corta la mayor ocurrencia de $cadena a la izquierda de laexpresión expr

${cadena%expr} Corta la menor ocurrencia de $cadena a la derecha de laexpresión expr

${cadena%%expr} Corta la mayor ocurrencia de $cadena a la derecha de laexpresión expr

${cadena/subcad1/subcad2} Cambia en $cadena la primera ocurrencia de subcad1 porsubcad2

${cadena//subcad1/subcad2}

Cambia en $cadena todas las ocurrencias de subcad1 porsubcad2

${cadena/#subcad1/subcad2}

Si subcad1 es igual al inicio de $cadena, entonces es cambiadapor subcad2

Si en una pregunta el S es ofrecido como valor default (patrón) y la salida va hacia la variable$SN, después de leer el valor podemos hacer:

SN=$(SN:-S}

De esta forma si el operador dió un simple <ENTER> para confirmar que aceptó el valor default,después de ejecutar esta instrucción, la variable tendrá el valor S, en caso contrário, tendrá el valortecleado.

Para saber el tamaño de una cadena:

$ cadena=0123 $ echo ${#cadena} 4

Para extraer de una cadena de la posición uno hasta el final hacemos:

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 80 / 123

Page 81: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ cadena=abcdef $ echo ${cadena:1} bcdef

Fíjate que el origen es cero y no uno.

En la misma variable $cadena del ejemplo de arriba, para extraer 3 caracteres a partir de la2ª posición:

$ echo ${cadena:2:3} cde

Fíjate que nuevamente el origen de la posición es cero y no uno.

Para suprimir todo a la izquierda de la primera ocurrencia de una cadena, haz:

$ cadena="Conversa de Bar" $ echo ${cadena#*' '} de Bar $ echo "Conversa "${cadena#*' '}Conversa de Bar

En este ejemplo fue suprimido a la izquierda todo lo que estuviera antes de la ocurrencia de laexpresión *' ', o sea, todo hasta el primer espacio en blanco.

Estos ejemplos también podrían ser escritos sin proteger el espacio de la interpretación del Shell(pero prefiero protegerlo para facilitar a legibilidad del código), mira:

$ echo ${cadena#* } de Bar $ echo "Conversa "${cadena#* } Conversa de Bar

Fíjate que en la construcción de expr está permitido el uso de metacaracteres.

Utilizando el mismo valor de la variable $cadena, observa como haríamos para tenersolamente Bar:

$ echo ${cadena##*' '} Bar $ echo "Vamos 'Chopear' en el "${cadena##*' '} Vamos 'Chopear' en elBar

Esta vez suprimimos a la izquierda de la cadena la mayor ocurrencia de la expresión expr. Asícomo en el caso anterior, el uso de metacaracteres está permitido.

Otro ejemplo mas útil: para que no aparezca el camino (path) completo de tu programa (que, comoya sabemos está contenido en la variable $0) en un mensaje de error, empieza tu texto de lasiguiente forma:

echo Uso: ${0##*/} texto del mensaje de error

En este ejemplo sería suprimido por la izquerda todo hasta la última barra (/) del camino (path),quedando solamente el nombre del programa.

* El uso de porcentaje (%) es como si mirasemos el simbolo (#) en el espejo, o sea, son simétricos.Veamos un ejemplo para probarlo:

$ echo $cadena Conversa de Bar $ echo ${cadena%' '*} Conversa de $ echo ${cadena%%' '*}Conversa

Para cambiar la primera ocurrencia de una sub-cadena en una cadena por otra:

$ echo $cadena Conversa de Bar $ echo ${cadena/de/en el} Conversa en el Bar $ echo${cadena/de /} Conversa Bar

En este caso presta atención cuando vayas a usar metacaracteres, son unos comilones! Siemprecombinarán con la mayor posibilidad, mira el ejemplo siguiente donde la intención era cambiarConversa de Bar por Charla de Bar:

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 81 / 123

Page 82: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ echo $cadena Conversa de Bar $ echo ${cadena/*a/Charla} Charlar

La idea era cogerlo todo hasta la primera a, pero lo que cambio fue todo hasta la última a. Estopodría resolverse de diversas formas, veamos algunas:

$ echo ${cadena/*sa/Charla} Charla de Bar $ echo ${cadena/????????/Charla} Charla de Bar

* Cambiando todas las ocurrencias de una subcadena por otra. Cuando hacemos:

$ echo ${cadena//a/o} Converso de Bor

Cambiamos todas las letras a por o. Otro ejemplo más útil es para contar la cantidad de archivosexistentes en el directorio en uso. Observa la linea siguiente:

$ ls | wc -l 30

Viste? El wc produce una cantidad de espacios en blanco al inicio. Para eliminarlos podemoshacer:

$ CtdArChs=$(ls | wc -l) # CtdArchs recibe la salida del comando $ echo ${CtdArChs// /} 30

En el último ejemplo, como sabía que la salida era compuesta de blancos y números, monté estaexpresión para cambiar todos los espacios por nada. Fíjate que después de las dos primerasbarras existe un espacio en blanco.

Otra forma de hacer la misma cosa sería:

$ echo ${CtdArChs/* /} 30

Cambiando una sub-cadena en el inicio o en el fin de una variable. Vamos a usar comoejemplo el conocido pájaro del campo "Quero quero", conocido en otros países como "Terotero". Para cambiarla al inicio hacemos:

$Pájaro="quero quero" $ echo $Pájaro quero quero $ echo "Como dice el gaucho -"${Pájaro/#quero/no} Como dice el gaucho - no quero

Para cambiarla al final hacemos:

$ echo "Como se dice en el norte - "${Pájaro/%quero/no} Como se dice en el norte - quero no

- Ahora basta, la conversación de hoy fue muy aburrida porque hay muchas cosas paramemorizar, así que lo principal es que hayas entendido lo que te dije y cuando lo necesites,consultes estas servilletas en las que escribí estas ayudas y después guárdalas para futurasconsultas. Pero volviendo a lo que importa, ha llegado la hora de tomar otro y ver el partido defutbol. Para la próxima te voy a aflojar un poco y solo te voy a pedir lo siguiente: toma la rutinapregunta.func, (de la cual hablamos en el inicio de nuestra conversa de hoy) y optimízala para quela variable $SN reciba el valor default por expansión de parámetros, como vimos.

- Mozo, no se olvide de mi y llene mi vaso.

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos quequien quiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 82 / 123

Page 83: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

-- HumbertoPina - 23 Jan 2007

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa009 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa009 83 / 123

Page 84: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa10Controles: - Última Atualização: [14 Feb 2012 - V.11]

Conversación de Bar X

El comando evalSeñales de Procesos

señales asesinasEl trap no atrapa

Comando getopts

-Que hay amigo, te lo puse mas fácil, verdad? Un ejercicio muy simple...

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 84 / 123

Page 85: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

- Si, pero en los tests que hice y de acuerdo con lo que me enseñaste sobre substitución deparámetros, me pareció que debería hacer otras alteraciones en las funciones que creamos, paradejarlas de uso más general como me dijiste que todas las funciones deberían de ser, quieres ver?

- Claro, si te pedí hacerlas es porque estoy con ganas de verte aprender, pero alto! dame unmomento!

- Mozo! Trae dos, uno sin espuma!

- Anda, enseñame lo que hiciste.

- Bien, además de lo que me pediste, me fije que el programa que llamaba la función, tendríaque tener previamente definida la línea en que sería dado el mensaje y la cantidad de columnas. Loque hice fue incluir dos líneas - en las cuales emplee sustitución de parámetros - y en caso de queuna de estas variables no fuese introducida, la propia función la generaría. La línea del mensajeestaría tres líneas encima del final de la pantalla y el total de columnas sería obtenido por elcomando tput cols. Mira como quedó:

$ cat pergunta.func # La función recibe 3 parámetros en el siguiente orden: # $1 - Mensaje a serdado 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-Za-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 msjde pantalla

- Me gustó, te anticipaste a lo que te iba a pedir. Solamente para cerrar esta conversación desustitución de parámetros, fíjate que la legibilidad es horrible, pero la optimización, o sea, lavelocidad de ejecución, está óptima. Como las funciones son cosas muy personales, ya que cadauno 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 lalógica saliendo de la memorización, pero te vuelvo a recordar, todo lo que te mostré la otra vezaquí en el Bar, es válido y de gran ayuda, guarda aquellas servilletas que escribimos que tarde otemprano te van a ser muy útiles.

El comando eval - Te voy a dar un problema que dudo que resuelvas:

$ var1=3 $ var2=var1

- Te dí estas dos variables, y quiero que me digas como puedo, solamente refiriéndome a$var2, listar el valor de $var1 (3).

- Ah! eso es fácil, es sólo hacer:

echo $`echo $var2`

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

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

$ echo $`echo $var2` $var1

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 85 / 123

Page 86: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

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

- Tu razonamiento realmente fue lógico, el problema es que te olvidaste de una de las primerascosas de que te hablé aquí en el Bar y voy a repetir. El Shell usa el siguiente orden para resolveruna 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 laejecución, la única variable existente era $var2 y por eso tu solución produjo como salida $var1. Elcomando 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 noexistiese la instrucción eval, cuya sintaxis es:

eval cmd

Donde cmd es una línea de comando cualquiera que se podría inclusive ejecutar directamente en elprompt del terminal. Cuando pones el eval al principio, lo que ocurre es que el Shell trata cmdcomo 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.

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` 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. Parala segunda pasada habría 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 listartistamandamsj.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 listamusicalistartista listartista3 logado logaute.sh

Nuevamente, al momento de la sustitución de las variables, $var1 todavía no se había presentadoal Shell para ser resuelta, por eso sólo nos queda ejecutar el comando eval para dar las dospasadas necesarias.

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

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 86 / 123

Page 87: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ cat fazmenu #!/bin/bash # # Lista numerando los programas con extensión .sh en # directorioactual y ejecuta el escogido por el operador # clear; i=1 printf "%11s\t%s\n\n" Opción ProgramaCASE='case $opt in' for arq in *.sh do printf "\t%03d\t%s\n" $i $arq CASE="$CASE "$(printf"%03d)\t %s;;" $i $arq) i=$((i+1)) done CASE="$CASE *) . error;; esac" read -n3 -p "Introduce laopción deseada: " opt echo eval "$CASE"

Parece complicado porque usé mucho el printf para formatear la pantalla, pero es bastantesimple, vamos a entenderlo: el primer printf fue colocado para hacer el encabezado y en seguidacomencé a montar dinámicamente la variable $CASE, sobre la cual al final será hecho un eval parala ejecución del programa escogido. Observa sin embargo que dentro del loop del for existen dosprintf: el primero sirve para formatear la pantalla y el segundo para montar el case (si antes delcomando read colocas una línea echo "$CASE", verás que el comando case montado dentro de lavariable está todo indentado. Una pasada, verdad? :). En la salida del for, fue agregada una líneaa la variable $CASE, para que en el caso de que se haga una opción no válida, sea ejecutada unafunció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.sh007 fazmenu.sh 008 logaute.sh 009 monbg.sh 010 readpipe.sh 011 redirread.sh Introduce laopción deseada:

En este programa sería interesante tener una opción de escape, y para eso sería necesario lainclusión de una línea después del loop de montaje de la pantalla y alterar la línea en la cualhacemos 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 # directorioactual y ejecuta el escogido por el operador # clear; i=1 printf "%11s\t%s\n\n" Opción ProgramaCASE='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 incluidaCASE="$CASE 999) exit;; # línea alterada *) ./error;; esac" read -n3 -p "Introduce la opcióndeseada: " opt echo eval "$CASE"

Señales de ProcesosExiste en Linux una cosa llamada señal (signal). Existen diversas señales que pueden sermandadas para (o generados por) procesos en ejecución. Vamos de aqui en adelante a dar unaojeada a las señales enviadas hacia los procesos y más adelante vamos a dar una pasada rápidapor las señales generados por procesos.

señales asesinas

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 delcomando kill, algunas secuencias de teclas también pueden generar sig. La tabla siguientemuestra las señales más importantes para monitorear:

Señales Más ImportantesSeñal Generado por:

0 EXIT Fin normal de programa

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 87 / 123

Page 88: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

15 SIGTERM Cuando recibe un kill o kill -TERM

0 EXIT Fin normal de programa1 SIGHUP Cuando recibe un kill -HUP2 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 es altamenteindeseable ya que muchos programas necesitan "limpiar el medio campo" a su término. Si el finalocurre de forma prevista, o sea si tiene un final normal, es muy fácil de hacer esta limpeza, sinembargo 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 archivosde trabajo inútilesEl procesador podrá quedar lleno de procesos zombies y defuncts generados por procesoshijos 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 dedatos 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 monitorizar lasterminaciones 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 lasseñales S1 S2 ... SN.

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

Para entender el uso de las comillas (") y los apóstrofes (') vamos a recurrir a un ejemplo que trataun fragmento de un script que hace un ftp hacia una máquina remota ($RemoComp), en la cual elusuá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 queeste script es muy usado por diversas pesonas de la instalación. Veamos este trozo del código:

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

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

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 88 / 123

Page 89: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

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

Así en el caso de que hubiese una interrupción brusca (señales 1, 2, 3 o 15), antes de que elprograma 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 tuviese la instrucción exit, al final de laejecución de esta línea el flujo del programa volvería al punto en que estaba cuando recibió la señalque 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, queborraría el archivo. Si la finalización es normal, la señal también será generada y el rm seráejecutado.

Observa también que el Shell analiza la línea de comandos, una vez cuando el trap esinterpretado (y es por eso que es usual colocarlo al inicio del programa) y nuevamente cuando serecibe alguna de las señales listadas. Entonces, en el último ejemplo, el valor de $$ será sustituídoen el momento que el comando trap es leído por primera vez, ya que las comillas (") no protegenel 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, elcomando debería estar colocado entre apóstrofes ('). Así, en la primera interpretación del trap, elShell no vería el signo de pesos ($), sin embargo los apóstrofes (') serian retirados y finalmente elShell 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

Suponte dos casos: tu tienes dos scripts que llamaremos script1, cuya primera línea será un trapy script2, siendo este último ejecutado por una llamada del primero, y por ser dos procesosdiferentes, 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 siocurriese una interrupcción (<CTRL+C> o <CTRL+\>) en el script2, la línea sólo seriainterpretada 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 elcaso de que la interrupción se diera durante la ejecución del script1, el archivo no habríasido creado, en el caso de que ocurriera durante la ejecución del script2, el valor de $$ seríael 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 siendomonitoreadas en el ambiente, así como la línea de comando que será ejecutada cuando talesseñales sean recibidas.

Si la línea de comandos del trap es nula (o sea vacía), esto significa que las señales

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 89 / 123

Page 90: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

especificadas deben ser ignoradas cuando sean recibidas. Por ejemplo, el comando:

trap "" 2

Especifica que la señal de interrupción (<CTRL+C>) debe ser ignorada. En ese caso no se deseaque la ejecución sea interrumpida. En el último ejemplo fíjate que el primer argumento debe serespecificado para que la señal sea ignorada, y no es equivalente a escribir lo siguiente, cuyafinalidad es de retornar la señal 2 a su estado patrón (default):

trap 2

Si se ignora una señal, todos los Subshells ignoraran esta señal. Por lo tanto, si tu especificas queacción debe ser tomada cuando se reciba una señal, entonces todos los Subshells tambiéntomaran la misma acción cuando reciban esta señal, o sea, las señales son automáticamenteexportadas. Para la señal que hemos mostrado (señal 2), significa que los Subshells seránfinalizados.

Suponte que ejecutes el comando:

trap "" 2

y entonces ejecutes un Subshell, que volverá a ejecutar otro script como un Subshell. Si segenerase una señal de interrupción, esta no tendrá efecto sobre el Shell principal ni sobre losSubshell por él llamados, ya que todos ellos ignorarán la señal.

Otra forma de restaurar una señal a su patrón (default) es haciendo:

trap - señal

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

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

El problema en este tipo de construcción es que en el caso de que el operador no supiese laseñal, probablemente haría un <CTRL+C> o un <CTRL+\> durante la instrucción read para detener elprograma y en el caso de que actúe así, cualquier cosa que escribiese no aparecería en la pantalladel terminal. Para evitar que eso pase, lo mejor a hacer es:

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

Para terminar este asunto, abre un terminal gráfico y escribe en el prompt de comando losiguiente:

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

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

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 90 / 123

Page 91: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Uno mas, porque no me puedo resistir... Ahora escribe esto:

$ trap "echo acabó" 17

En seguida haz:

$ sleep 3 &

Acabas de crear un subshell que dormirá durante tres segundos en background. Al final de estetiempo, recibirás un mensaje acabó, porque la señal 17 es emitida cada vez que un subshelltermina su ejecución.

Para volver estas señales a sus opciones por defecto, haz:

$ trap 17 28

O:

$ trap - 17 28

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

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, verdad? Si tu descubres algun caso interesante del uso de señales, porfavor informame por e-mail porque es muy rara la literatura sobre el asunto.

Comando getoptsEl comando getopts recupera las opciones y sus argumentos de una lista de parámetros deacuerdo 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 puedenagrupar. Debes usar este comando para "cortar en partes" opciones y argumentos pasados haciatu script.

Sintáxis:

getopts cadenadeopciones nombre

La cadenadeopciones debe explicitar una cadena de caracteres con todas las opcionesreconocidas por el script, así si este reconoce las opciones -a -b y -c, cadenadeopciones debe serabc. Si deseas que una opción sea seguida por un argumento, coloca dos puntos (:) después dela 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 (?).

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 91 / 123

Page 92: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

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

getopts coloca un signo de interrogación (?) en la variable definida en nombre si encuentra unaopción no definida en cadenadeopciones o si no encuentra el argumento esperado para unadeterminada 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 getoptsobtiene una opción, almacena el índice del próximo parámetro a ser procesado en la variableOPTIND.

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

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

El ejemplo siguiente es meramente didáctico, y sirve para mostrar, en un pequeño fragmento decódigo, el uso pleno del comando.

$ cat getoptst.sh #!/bin/sh

# Ejecute así: # # getoptst.sh -h -Pimpressora arch1 arch2 # # y note que las informaciones detodas las opciones son exhibidas # # La cadena 'P:h' dice que la opción -P es una opcióncompleja # 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' OPTARGes '' getopts hizo la variable OPT_LETRA igual a 'P' OPTARG es 'impresora' Ignorando losprimeros $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 respectivosargumentos, dejando solamente los parámetros que fueron pasados por el operador para untratamiento posterior .

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

$ getoptst.sh -h -P impresora arch1 arch2 getopts hizo la variable OPT_LETRA igual a 'h' OPTARGes '' getopts hizo la variable OPT_LETRA igual a 'P' OPTARG es 'impresora' Ignorando losprimeros $OPTIND-1 = 3 argumentos Lo que sobró de la línea de comandos fue 'arch1 arch2'

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 92 / 123

Page 93: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

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 ./getoptst.sh: illegal option -- fgetopts hizo la variable OPT_LETRA igual a '?' OPTARG es '' getopts hizo la variable OPT_LETRAigual a 'P' OPTARG es 'impresora' Ignorando los primeros $OPTIND-1 = 2 argumentos Lo quesobró 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 fuedidáctico, pero imagina un programa que aceptase muchas opciones y sus parámetros podrían noestar pegados a las opciones, sus opciones también podrían o no estar pegadas, iba a ser uncase 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. Sera porque ya estoymedio cansado con tanta información nueva en mi cabeza. Vamos a tomar la del estribo o todaviaquieres explicar alguna particularidad mas del Shell?

- Ni lo uno ni lo otro, yo también me cansé, pero hoy no voy a tomar la del estribo porque estoyyendo 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 deuna ventana gráfica, en el centro no aparece dinámicamente en vídeo inverso la cantidad de líneasy columnas? Entonces! Quiero que reproduzcas eso usando el lenguaje Shell.

- Mozo, traeme rapidito mi cuenta! Voy a contar hasta uno y si no me la trajiste me voy!

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos quequien quiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima

-- HumbertoPina - 31 Jan 2007

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa10 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa10 93 / 123

Page 94: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBarConversa011Controles: - Última Atualização: [14 Feb 2012 - V.15]

Conversación de Bar parte XI

Named PipesSincronización de procesos.Bloqueo de archivosSubstitución de procesos

- Ei!, como va amigo, todo bien?

- Más o menos.. te acuerdas que me pediste que hiciera un programa que cuando el tamaño dela pantalla variase, en el centro apareciese dinamicamente y en vídeo inverso, la cantidad de líneas

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 94 / 123

Page 95: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

y columnas, de la misma forma que el Linux hace normalmente?. Bueno, lo hice, pero la aparienciano quedó igual.

- No estoy preocupado por la apariencia, lo que yo quería es que ejercitases lo que aprendimos.Déjame ver lo que hiciste.

$ cat tamtela.sh #!/bin/bash # # Colocar en el centro de la pantalla, con vídeo inverso, # unacantidad de columnas y lineas # cuando el tamaño de la pantalla es alterado. # trap Muda 28 # 28= señal generada por el cambio de tamaño # de la pantalla y Muda es la función que hace eso.

Bold=$(tput bold) # Negrita, modo de énfasis Rev=$(tput rev) # Modo de vídeo inverso Norm=$(tputsgr0) # Restaura la pantalla al valor por defecto

Muda () { clear Cols=$(tput cols) Lins=$(tput lines) tput cup $(($Lins / 2)) $(((Cols - 7) / 2)) # Centrode la pantalla echo $Bold$Rev$Cols X $Lins$Norm }

clear read -n1 -p "Cambie el tamaño de la pantalla o teclee algo para terminar"

- Perfecto!, que se joda la apariencia, después te enseño otras formas de mejorarlo, lo que valees el programa, está funcionando y esta todo optimizado.

- Pero perdí la mayor parte del tiempo intentando descubrir como aumentar el tamaño de lafuente.- ...

- Deja eso para otro día, hoy vamos a ver unas cosas bastante interesantes y útiles..

Named PipesOtro tipo de pipes es el "named pipes", que también es llamado por FIFO. FIFO es un acrónimo deFirst In First Out, que se refiere a la propiedad de que los bytes salen con el mismo orden queentran. El "name" en named pipe es en verdad el nombre de un archivo. Los archivos tipo namedpipe son mostrados por el comando "ls" como cualquier otro, con pocas diferencias, mira:

$ ls -l pipe1 prw-r-r-- 1 julio dipao 0 Jan 22 23:11 pipe1|

La p en la columna del lado izquierdo indica que pipe1 es un named pipe. El resto de los bits decontrol de permisos, que se pueden leer o grabar al pipe, funcionan como un archivo normal. Enlos sistemas mas modernos una barra vertical (|) colocada al final del nombre del archivo, es otrapista y en los sistemas LINUX, donde la opción de color esta habilitada, el nombre del archivo seescribe en rojo por defecto.

En los sistemas mas antiguos, los named pipes son creados por el programa mknod, normalmentesituado en el directorio /etc.

En los sistemas mas modernos, la misma tarea es hecha por mkfifo. EL programa mkfifo recibeuno o mas nombres como argumento y crea pipes con esos nombres. Por ejemplo , para crear unnamed pipe con nombre pipe1 haz:

$ mkfifo pipe1

Como siempre la mejor forma de mostrar que algo funciona es dando ejemplos. Suponga quehayamos creado el named pipe mostrado anteriormente. Vamos ahora a trabajar con dossecciones o dos consolas virtuales o una de cada una. En una de ellas haz:

$ ls -l > pipe1

en la otra haz:

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 95 / 123

Page 96: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

$ cat < pipe1

Voilá! La salida del comando ejecutado en la primera consola fue mostrada en la segunda. Fíjateque el orden en que los comandos ocurrieron no importa.

Si prestaste atención, viste que el primer comando ejecutado parecía estar "colgado". Estosucede por que la otra punta del pipe todavía no estaba conectada, y entonces el sistemaoperativo suspendió el primer proceso hasta que el segundo proceso "abriera" el pipe. Para queun proceso que usa el _pipe no quede en modo wait, es necesario que en una punta del pipetenga un proceso "que habla" y en el otro un proceso " que escucha" y en el ejemplo que dimos , ells era el que hablaba y el cat era el que escuchaba.-

Una aplicación muy útil de los named pipes es permitir que programas sin ninguna relación sepuedan comunicar entre sí, los named pipes también son usados para sincronizar procesos, ya queen un determinado punto puedes colocar un proceso para "escuchar" o "hablar" en un determinadonamed pipe y solo saldrá de allí , si otro proceso "habla" o "escucha" en aquel pipe.

Viste que el uso de esta herramienta es ideal para sincronizar los procesos y para bloqueararchivos y poder evitar así perdida o corrupción de información debido a actualizacionessimultáneas (concurrencia). Veamos ejemplos para ilustrar estos casos.

Sincronización de procesos.

Imagina que lanzas paralelamente dos programas (procesos), los diagramas de bloque de susrutinas son como muestra la siguiente figura:

Los dos procesos son lanzados en paralelo y en el BLOCO1 del Programa1 las tres clasificacionesson lanzadas de la siguiente manera:

for Arq in BigFile1 BigFile2 BigFile3 do if sort $Arq then Manda=va else Manda=pare break fi done echo $Manda > pipe1 [ $Manda = pare ] && { echo Error durante la clasificación de los archivos exit 1 } ...

De esta forma, el comando if verifica cada clasificación que está siendo efectuada. En caso deque ocurra un problema, las clasificaciones siguientes serán abortadas, un mensaje conteniendo lacadena pare es enviada por el pipe1 y el programa1 es finalizado con un fin anormal.

Mientras el Programa1 ejecutaba su primero bloque (las clasificaciones) el Programa2 ejecutaba subloque BLOCO1, procesando sus rutinas de apertura y menú paralelamente al Programa1, ganandode esta forma un buen intervalo de tiempo.

El fragmento del código del Programa2 que vemos a continuación, muestra la transición del BLOCO1hacia el BLOCO2:

OK=`cat pipe1`

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 96 / 123

Page 97: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

if [ $OK = va ] then ... Rutina de impresión ... else # Recibí "pare" en OK exit 1 fi

Después de la ejecución del primer bloque , el Programa2 pasará a "escuchar" el pipe1, quedandoparado hasta que las clasificaciones del Programa1 terminen, comprobando a continuación elmensaje pasado por el pipe1 para decidir si los archivos están preparados para ser impresos , osi el programa debería terminarse. De esta forma es posible lanzar programas de formasincronizada y no sincronizada cuando es necesario, ganando bastante tiempo de procesamiento.

Bloqueo de archivos

Supón que escribes una CGI (Common Gateway Interface) en Shell para contar cuantos hitsrecibe una determinada URL y la rutina del contador es la siguiente:

Hits="$(cat page.hits 2> /dev/null)" || Hits=0 echo $((Hits=Hits++)) > page.hits

De esa forma si la página recibe dos o mas accesos concurrentes, uno o mas podrá(n) perderse,basta que el segundo acceso sea hecho después de una lectura del archivo page-hits y antes desu grabación, es decir, basta que el segundo acceso sea hecho después de que el primero hayaejecutado la primer línea del _script_y antes de ejecutar la segunda. Entonces que hacemos? Pararesolver el problema de concurrencia vamos a utilizar un named pipe. Creamos el siguiente scritpque será el daemon que recibirá la pagina en nuestro site que necesita de un contador.

$ cat contahits.sh #!/bin/bash

PIPE="/tmp/pipe_contador" # archivo llamado pipe # dir donde serán colocados los archivoscontadores de cada pagina DIR="/var/www/contador"

[ -p "$PIPE" ] || mkfifo "$PIPE"

while : do for URL in $(cat < $PIPE) do FILE="$DIR/$(echo $URL | sed 's,.*/,,')" # OBS1: en el sedarriba, como precisaba buscar # una barra,usamos coma como separador. # OBS2: cuando rodarcomo daemon comente la próxima línea echo "arquivo = $FILE"

n="$(cat $FILE 2> /dev/null)" || n=0 echo $((n=n+1)) > "$FILE" done done

Como solamente este script modifica los archivos, no existe problema de concurrencia

Este script será un daemon, esto es, correrá en background. Cuando una página sufre un acceso,el script escribirá su URL en el archivo del pipe. Para probarlo, ejecuta este comando:

echo "test_pagina.html" > /tmp/pipe_contador

Para evitar errores, en cada página que quisiéramos agregar el contador añadiriamos la siguientelinea:

<!--#exec cmd="echo $REQUEST_URI > /tmp/pipe_contador"-->

Observa que la variable $REQUEST_URI contiene el nombre del archivo que el navegador(browser)pidió. Este último ejemplo ,es fruto de una idea que intercambié con un amigo y maestroen Shell , Thobias Salazar Trevisan que escribió el script y lo colocó en su excelente URL.Aconsejo a todos que los que quieren aprender Shell que le echen un vistazo y la incluyan en sus

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 97 / 123

Page 98: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

favoritos.

Aja! Pensaste que el asunto sobre los named pipes estaba terminado? Pues estabas engañado. Acontinuación te voy a mostrar un uso diferente de ellos.

Substitución de procesos

Acabo de mostrarte un montón de recetas sobre los named pipes . Ahora te voy a mostrar que elShell también usa los named pipes de una manera bastante singular, que es la sustitución deprocesos (process substitution). Una sustitución de procesos ocurre cuando dentro de un pipelinede comando pones un comando entre paréntesis y un < o un > unido al paréntesis de la izquierda.Por ejemplo, tecleando el comando:

$ cat <(ls -l)

Resultará que el comando ls -l ejecutado en un subshell como es normal (por estar entreparéntesis), redireccionará la salida a un named pipe temporal, que el Shell crea, usa y luegoelimina. Entonces el cat tendrá un nombre de archivo válido para leer (que será este named pipe ycuyo dispositivo lógico asociado es /dev/fd/63), y tendremos la misma salida que la generadapor la del ls -l, pero dando uno o mas pasos de lo usual, y esto es mas costoso para elcomputador.

Como podremos constatar esto? Fácil .. Mira el siguiente comando:

$ ls -l >(cat) l-wx------ 1 jneves jneves 64 Aug 27 12:26 /dev/fd/63 -> pipe:[7050]

Y... Realmente es un named pipe.

Debes estar pensando que esto es una locura nerd , no? Entonces supongamos que tenes 2directorios: dir y dir.bkp , y deseas saber si los dos son iguales ( aquella vieja duda: estará mibackup actualizado? ). Basta comparar los dos archivos de los directorios con el comando cmp,haciendo:

$ cmp <(cat dir/*) <(cat dir.bkp/*) || echo backup desactualizado!

o, mejor todavía:

$ cmp <(cat dir/*) <(cat dir.bkp/*) >/dev/null || echo backup desactualizado!

De la última forma, la comparación fue efectuada en todas las líneas de todos los archivos deambos directorios. Para acelerar el proceso, podríamos comparar solamente el listado largo deambos en los directorios, pues cualquier modificación que un archivo sufra, es mostrado comoalteración de la fecha/hora y/o del tamaño del archivo. Mira como quedaría:

$ cmp <(ls -l dir) <(ls -l dir.bkp) >/dev/null || echo backup desactualizado!

Este es un ejemplo meramente didáctico, pues son tantos los comando que producen mas de unalínea de salida que sirve como guia para otros. Quiero generar una lista de mis archivos,numerados y al final dar el total de archivos del directorio actual:

while read arq do ((i++)) # así no es necesario inicializar i echo "$i: $arq" done < <(ls) echo "En el directório corriente (`pwd`) existen $i archivos"

Está bien, yo sé que existen otras formas de ejecutar la misma tarea. Usando el comando while.

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 98 / 123

Page 99: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

La forma mas común de resolver este problema sería:

ls | while read arq do ((i++)) # así no es necesario inicializar i echo "$i: $arq" done echo "En el directório corriente (`pwd`) existen $i archivos"

Cuando ejecutase el script, parecería que esta todo ok, sin embargo en el comando echo despuésdel none, vas a ver que el valor de $i se perdió. Esto se debe al hecho de que esta variable estasiendo incrementada en un subshell creado por el pipe (|) y que termina con el comando done,llevándose con él todas las variables creadas en su interior y las alteraciones hechas en todas lavariables, inclusive las creadas externamente. Solamente para mostrarte que una variable creadafuera del subshell y alterada en su interior, pierde las alteraciones hechas al final, ejecuta el scriptsiguiente:

#!/bin/bash LIST="" # Creada en el shell principal ls | while read FILE # Inicio del subshell do LIST="$FILE $LIST" # Alterada dentro del subshell done # Fin del subshell echo :$LIST:

Al final de la ejecución vas a ver que aparecerán apenas dos puntos (::). Pero en el inicio de esteejemplo te dije que era meramente didáctico, ya que existen formas mejores de hacer la mismatarea. Mira estas dos:

$ ls | ln

o entonces, usando la propia substitución de procesos:

$ cat -n <(ls)

Un último ejemlo: tu deseas comparar arq1 y arq2 usando el comando comm, pero este comandonecesita que los archivos estén clasificados. Entonces la mejor forma de proceder es:

$ comm <(sort arq1) <(sort arq2)

Esta forma evita que tengas que hacer las siguientes operaciones:

$ sort arq1 > /tmp/sort1 $ sort arq2 > /tmp/sort2 $ comm /tmp/sort1 /tmp/sort2 $ rm -f /tmp/sort1/tmp/sort2

Gente.. nuesta Convesación llegó a su fin, . Disfruté mucho aquí y recibí diversos elogios por lostrabajos realizados a lo largo de 12 meses y, lo mejor de todo, hice muchas amistades y tomémuchos chopps gratis con los lectores que encontré por los congresos y charlas que andohaciendo por nuestro querido Brasíl. Lo que voy a escribir aquí no está arreglado ni sé si serápublicado, pero como los editores de esta revista son dos locos hermosos (ambos Rafael), esposible que lo dejen pasar. Es lo siguiente: si quieren que el Papo de Botequin continue, llenen lacaja postal de la Linux Magazine pidiendo esto y desde ya escoja el próximo tema entre sed +expresiones regulares o lenguaje awk.

De cualquier forma, en caso de que no consigamos sensibilizar la dirección de la revista, medespido de todos mandando un abrazo a los barbudos y besos a las chicas y agradezco a los masde 100 mails que recibí (todos elogiosos) y todos debidamente respondidos. A la salud de todosnosotros: Chin, chin

-Chico, cierra la cuenta que voy a cambiar de bar.

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 99 / 123

Page 100: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar malde los políticos lo único que tienes que hacer es mandarme un e-mail para [email protected] aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos quequien quiera hacer un curso nota diez de programación en Shell que mande un e-mail [email protected] para informarse.

Gracias y hasta la próxima!

-- DanielRefosco - 3 Jan 2007

sex shopsex shopsex shopsex shoplingeriesex shopsex shopsex shop atacadodicas desexocalcinhasuniformes profissionaisuniformescamisetas atacadocamisetas

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBarConversa011 > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBarConversa011 100 / 123

Page 101: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

[ 09 Feb 2014 - 06:45 ] Links AmigosBR-LinuxViva o LinuxDicas-LAurelioThobias

1. TWiki BásicoPágina InicialÚltimas AlteraçõesÍndiceProcuraEstatísticas de UsoAviso de AtualizaçãoMapa do Site

2. TWiki AvançadoRegistre-seConfigurações GeraisQuem SomosRegras de FormataçãoBiblioteca GráficaCarinhas Gráficas

3. Projeto GráficoPré TópicoPós TópicoMenu LateralMenu de NavegaçãoCSS UtilizadoBotões EspeciaisIndica Onde EstamosCabeçalho PadrãoCopy Right/Left

Você está aqui: TWikiBar > TWikiBaracompanhamentosControles: - Última Atualização: [16 May 2012 - V.10]

Acompañamientos para el aperitivoEn construcción para siempre!

Esta página, a pesar de estar dentro del contenido de Conversaciones de Bar, nunca fuepublicada en la Linux Magazine. Trata de artículos que escribí para otras publicaciones, ayudasútiles que leí navegando por la internet (y en este caso con los debidos créditos), contribuciones deestas personas maravillosas y siempre dispuestas a ayudar del Software Libre, y de laimprescindible "Lista de Shell Script"

Pasando parámetros con xargs

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 101 / 123

Page 102: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Opciones del xargsOpción -iOpción -nOpción -pOpción -tResumen

Here StringsRotatório de TiagoAritmética en Shell

El uso de bcOtras formas de trabajar con enterosBaseando

Pruebas usando expresiones regularesColoreando la pantallaGanando la partida con más comodinesUsando el awk para buscar por equivalenciasfind – Buscando archivo por característicasAlgunas implementaciones de Bash 4.0

CoprocessosVectores asociativosLeyendo un archivo para un vectorCaja baja para alta y viceversa

Usando ampliaciones de parámetroUsando declaratives

Nuevas implementaciones en el comando caseExpansión de llaves

Pasando parámetros con xargsExiste un comando, cuya función principal es construir listas de parámetros y pasarlas para laejecución de otros programas o instrucciones. Este comando es el xargs y debe ser usado de lasiguiente manera:

xargs [comando [argumento inicial]]

En el caso de que el comando (que puede ser inclusive un script Shell), sea omitido, será usadopor default el echo.

El xargs combina el argumento inicial con los argumentos recibidos de la entrada patrón, deforma de ejecutar el comando especificado una o más veces.

Ejemplo:

Vamos a buscar una cadena de caracteres en todos los archivos dentro de un determinadodirectorio, usaremos el comando find con la opción -type f para buscar solamente los archivosnormales, despreciando directorios, archivos especiales, archivos de uniones, etc, y vamos ahacer la busqueda lo más general posible, recibiendo el nombre del directorio inicial y la cadena aser buscada como parámetros. Para eso hacemos:

$ cat grepr # # Grep recursivo # Busca la cadena de caracteres definida en $2 a partir deldirectorio $1 # find $1 -type f -print|xargs grep -l "$2"

Ejecutando este script buscamos, a partir del directorio definido en la variable $1, todos losarchivos que contengan la cadena definida en la variable $2.

Exactamente lo mismo podría hacerse si la línea del programa fuera la siguiente:

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 102 / 123

Page 103: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

find $1 -type f -exec grep -l "$2" {} \;

El primer proceso tiene dos grandes desventajas sobre el anterior:

La primera es bastante visible: el tiempo de ejecución de este método es muy superior alsegundo, eso porque el grep será hecho en cada archivo que le sea pasado por el find, unoa uno, al paso que con el xargs, será pasada toda, o en la peor de las hipótesis, la mayorparte posible, de la lista de archivos generada por el find;

Dependiendo de la cantidad de archivos encontrados que atiendan al find, podemos recibiraquel famoso y fatídico mensaje de error Too many arguments indicando una sobrecarga enla pila de ejecución del grep. Como fue dicho en el ítem anterior, si usamos el xargs, éstepasará hacia el grep la mayor cantidad de parámetros posible, suficiente para no causareste error y en caso necesario, ejecutará el grep más de una vez.

Atención aquellas personas de linux que usan el ls colorido como estándar: en los ejemplossiguientes que incluyen esta instrucción, deben usar la opción --color=none, en caso contrario,existen grandes posibilidades de que los resultados no sean los esperados (;-).

Vamos ahora a analizar un ejemplo que es más o menos lo contrario de este que acabamos dever. Esta vez, vamos a hacer un script para borrar todos los archivos del directorio actual y quepertenezcan a un determinado usuario.

La primera idea que surge es, como en el caso anterior, usar un comando find, de la siguientemanera:

find . -user cara -exec rm -f {} \;

Casi estaría correcto, el problema es que de esta forma se estarían borrando no solamente losarchivos de cara en el directorio actual, sino también de todos los otros sub-directorios "colgados"a éste. Veamos entonces como hacerlo correctamente:

ls -l | grep " cara " | cut -c55- | xargs rm

De esta forma, el grep seleccionó los archivos que contenían la cadena cara en el directorio actuallistado por el ls -l. El comando cut tomo solamente el nombre de los archivos, pasándolos haciael borrado, a cargo del rm usando el comando xargs como puente

El xargs es también una excelente herramienta de creación de one-liners (scripts de solamenteuna línea). Mira éste para listar todos los propietarios de archivos (inclusive sus links) "colgados"en el directorio /bin y sus sub-directorios.

$ find /bin -type f -follow | \ xargs ls -al | tr -s ' ' | cut -f3 -d' ' | sort -u

Muchas veces el /bin es un link (si no estoy equivocado, en Solaris es así) y la opción -followsobliga al find a seguir el link. El comando xargs alimenta el ls -al y la secuencia de comandossiguiente es para tomar solamente el 3er campo (propietario) y clasificarlo devolviendo solamenteuna vez cada propietario (opción -u del comando sort, que equivale al comando uniq).

Opciones del xargs

Las opciones del xargs pueden se usadas para construir comandos extremamente poderosos.

Opción -i

Para ejemplificar esto y comenzar a entender las principales opciones de esta instrucción, vamos

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 103 / 123

Page 104: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

a suponer que tenemos que borrar todos los archivos con extensión .txt en el directorio actual ypresentar sus nombres en pantalla. Veamos que podemos hacer:

$ find . -type f -name "*.txt" | \ xargs -i bash -c "echo borrando {}; rm {}"

La opción -i del xargs cambia pares de llaves ({}) por la cadena que está recibiendo a través delpipe (|). Entonces en este caso las llaves ({}) serán cambiadas por los nombres de los archivosque satisfagan al comando find.

Opción -n

Veamos este pequeño juego que vamos a hacer con el xargs:

$ ls | xargs echo > arch.ls $ cat arch.ls arch.ls arch1 arch2 arch3 $ cat arch.ls | xargs -n1 arch.lsarch1 arch2 arch3

Cuando mandamos la salida del ls hacia el archivo usando el xargs comprobamos lo que yadijimos, o sea, el xargs manda todo lo que sea posible (lo suficiente para no generar unasobrecarga en la pila) de una vez sola. En seguida, usamos la opción -n 1 para listar uno por uno.Sólo para estar seguros, mira el ejemplo siguiente, donde listaremos dos archivos en cada línea:

$ cat arch.ls | xargs -n 2 arch.ls arch1 arch2 arch3

Sin embargo, la línea de arriba podría (y debería) ser escrita sin usar el pipe (|), de la siguienteforma:

$ xargs -n 2 < arch.ls

Opción -p

Otra excelente opción del xargs es -p, en la cual el sistema pregunta si tu realmente deseasejecutar el comando. Digamos que en un directorio tengas archivos con la extensión .bug y .ok, los.bug tienen problemas que después de corregidos son grabados como .ok. Echa una mirada a lalista de este directorio:

$ ls dir arch1.bug arch1.ok arch2.bug arch2.ok ... arch9.bug arch9.ok

Para comparar los archivos buenos con los defectuosos, hacemos:

$ ls | xargs -p -n2 diff -c diff -c arch1.bug arch1.ok ?...y .... diff -c arch9.bug arch9.ok ?...y

Opción -t

Para finalizar, el xargs también tenemos la opción -t, donde va mostrando las instrucciones quemontó antes de ejecutarlas. Me gusta mucho esta opción para ayudar a depurar el comando quefue montado.

Resumen

Entonces podemos resumir el comando de acuerdo con la siguiente tabla:

Opción

Acción

-i Substituye el par de llaves ({}) por las cadenas recibidas

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 104 / 123

Page 105: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

-t Muestra la línea de comando montada antes de ejecutarla

-nNum Manda el máximo de parámetros recibidos, hasta el máximo de Num para que elcomando sea ejecutado

-lNum Manda el máximo de líneas recibidas, hasta el máximo de Num para que el comandosea ejecutado

-p Muestra la línea de comando montada y pregunta si desea ejecutarla

Here StringsPrimero un programador con complejo de inferioridad creó el redireccionamiento de entrada y lorepresentó con un signo de menor (<) para representar sus sentimientos. En seguida, otrosintiéndose todavia peor, creó el here document representándolo por dos signos de menor (<<)porque su complejo era mayor. El tercero, pensó: "estos dos no saben lo que es estardeprimido"... Entonces creó el here strings representándolo por tres signos de menor (<<<).

Bromas a parte, el here strings es utilísimo y, no sé porque, es un perfecto desconocido. En lapoquísima literatura que hay sobre el tema, se nota que el here strings es frecuentemente citadocomo una variante del here document, teoría con la que discrepo pues su aplicabilidad estotalmente diferente de aquella. Su sintaxis es simple:

$ comando <<< $cadena

Donde cadena es expandida y alimenta la entrada primaria (stdin) de comando.

Como siempre, vamos directo a los ejemplos de los dos usos más comunes para que vosotrosmismos saquéis las conclusiones.

Uso #1. Substituyendo la tan usada construcción echo "cadeia" | comando, que obliga a unfork, creando un subshell y aumentando el tiempo de ejecución.

Ejemplos:

$ a="1 2 3" $ cut -f 2 -d ' ' <<< $a # Normalmente se hace: echo $a | cut -f 2 -d ' ' 2 $ echo$Nomearch Mis Documentos # Arrrghhh! $ tr "A-Z " "a-z_" <<< $Nomearch # Substituyendo elecho $Nomearch | tr "A-Z " "a-z_" mis_documentos $ bc <<<"3 * 2" 6 $ bc <<<"scale = 4; 22 / 7"3.1428

Para mostrar la mejoría en el desempeño, vamos a hacer un loop de 500 veces usando el ejemplodado para el comando tr: Veamos ahora esta secuencia de comandos con medidas de tiempo:

$ time for ((i=1; i<= 500; i++)); { tr "A-Z " "a-z_" <<< $Nomearch >/dev/null; } real 0m3.508s user0m2.400s sys 0m1.012s $ time for ((i=1; i<= 500; i++)); { echo $Nomearch | tr "A-Z " "a-z_">/dev/null; } real 0m4.144s user 0m2.684s sys 0m1.392s

Veamos ahora esta otra secuencia de comandos con medidas de tiempo:

$ time for ((i=1;i<=100;i++)); { who | cat > /dev/null; } real 0m1.435s user 0m1.000s sys 0m0.380s $time for ((i=1;i<=100;i++)); { cat <(who) > /dev/null; } real 0m1.552s user 0m1.052s sys 0m0.448s $time for ((i=1;i<=100;i++)); { cat <<< $(who) > /dev/null; } real 0m1.514s user 0m1.056s sys0m0.412s

Observando este cuadro verás que en el primero, usamos la forma convencional, en el segundousamos un named pipe temporal para ejecutar una substitución de procesos y en el tercerousamos here strings. Notaras también que al contrario del ejemplo anterior, aqui el uso de herestrings no fue lo más veloz. Pero observad también que en este último caso el comando who está

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 105 / 123

Page 106: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

siendo ejecutado en un subshell y eso aumentó el proceso como un todo.

Veamos una forma rápida de insertar una línea como encabezamiento de un archivo:

$ cat num 1 2 3 4 5 6 7 8 9 10 $ cat - num <<< "Impares Pares" Impares Pares 1 2 3 4 5 6 7 8 9 10

Uso #2. Otra buena forma de usar el here strings es juntándolo con un comando read, noperdiendo de vista lo que aprendimos sobre IFS (vedlo aquí, en la explicación del comandofor). El comando cat con las opciones -vet muestra el <ENTER> como $, el <TAB> como ^I ylos otros caracteres de control con la notación ^L donde L es una letra cualquiera. Veamosentonces el contenido de una variable y después vamos a leer cada uno de sus campos:

Ejemplos:

$ echo "$línea" Leonardo Mello (21)3313-1329 $ cat -vet <<< "$línea" Leonardo Mello^I(21)3313-1329$ # Los separadores son blanco y <TAB> (^I) $ read Nom SNom Tel <<< "$línea" $ echo"${Nom}_$S{Nom}_$Tel" # Vamos a ver si leyó cada uno de los camposLeonardo_Mello_(21)3313-1329 # Leyó porque los separadores eran iguales al IFS

También podemos leer directamente de un vector (array) vedlo:

$ echo $Frutas Pera:Uva:Manzana $ IFS=: $ echo $Frutas Pera Uva Manzana # Sin las comillas elshell muestra el IFS como blanco $ echo "$Frutas" Pera:Uva:Manzana # Ahhh, ahora sí! $ read -aaFrutas <<< "$Frutas" # La opción -a del read, lee un vector $ for i in 0 1 2 > do > echo${aFrutas[$i]} # Imprimiendo cada elemento del vetor > done Pera Uva Manzana

Rotatório de TiagoEstaba, como lo hago todos los días dandole un repaso a los e-mails de la "Lista de Shell Script" ,cuando descubro algo totalmente inusitado de Tiago Barcellos Peczenyj.

Cuando resolví crear este conjunto de consejos, me acordé de eso y le pedí para que me enviaraaquel e-mail nuevamente. El texto siguiente es el e-mail que me mandó, solo inserté el últimoejemplo y saqué las abrebiaturas.

Julio descubrí una forma en la que el Shell crea combinaciones haciendo rotación con loselementos estipulados. Podemos generar todos los binarios de 0000 a 1111 de la siguienteforma:

$ A={0,1} $ eval echo $A$A$A$A 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 10101011 1100 1101 1110 1111

Una aplicación práctica que veo es para combinar valores diferentes sin tener que encadenarloops ni usar el =seq

$ A={`seq -s , -f "_%g" 3`} $ eval echo -e $A$A$A |tr ' _' '\n ' | grep -vE '.+?(\b[0-9]+\b).+?\1' 1 2 3 13 2 2 1 3 2 3 1 3 1 2 3 2 1

En este caso combiné los números de 1 a 3 eliminando repeticiones con el grep. usé un trfoca'podre' para tratar mejor los datos, saltando línea. El grep es simple como puedes notar,compruebo si una determinada parte de la combinación (.+?(\b[0-9]+\b).+?) existe en otra parte(\1), si existe, no la dejo imprimir por causa de la opción -v, y así

1 1 2 1 2 1 1 1 1

no son impresos.

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 106 / 123

Page 107: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Ahora va mi ejemplo: el one-liner siguiente genererá todos los permisos posibles (en octal) para elarchivo arq (el ejemplo fué interrumpido por que existen 512 combinaciones de permisosposibles).

$ A={`seq -s , 0 7`} $ eval echo -e $A$A$A | tr ' ' '\n' | xargs -i bash -c "chmod {} arq; ls -l arq" ----------1 julio julio 100 2006-11-27 11:50 arq ---------x 1 julio julio 100 2006-11-27 11:50 arq --------w- 1 juliojulio 100 2006-11-27 11:50 arq --------wx 1 julio julio 100 2006-11-27 11:50 arq -------r-- 1 julio julio100 2006-11-27 11:50 arq -------r-x 1 julio julio 100 2006-11-27 11:50 arq -------rw- 1 julio julio 1002006-11-27 11:50 arq -------rwx 1 julio julio 100 2006-11-27 11:50 arq . . . . . . . . . . . . . . . . . . -rwxrwxrw- 1 julio julio 100 2006-11-27 11:50 arq -rwxrwxrwx 1 julio julio 100 2006-11-27 11:50 arq

Veamos este ejemplo paso a paso para entenderlo:

$ echo $A {0,1,2,3,4,5,6,7} $ eval echo -e $A$A$A 000 001 002 003 004 005 006 007 010 ... ...767 770 771 772 773 774 775 776 777 $ eval echo -e $A$A$A | tr ' ' '\n' # El tr cambiará cadaespacio en blanco por um <ENTER> 000 001 002 003 . . . 774 775 776 777

A continuación el xargs (clique para consejos sobre el xargs) ejecuta el comando bash -c (quesirve para ejecutar una línea de comandos) que por cada vez que se ejecuta chmod y el ls -lpermite mostrar que los permisos están siendo alterados.

Aritmética en ShellAntiguamente usábamos el comando expr para hacer operaciones aritméticas y mucha gente aunla usa, pues es compatible con cualquier ambiente.

Ejemplo:

$ expr 7 \* 5 / 3 # 7 veces 5 = 35 dividido por 3 = 11 14

En este articulo podremos ver otras formas no tan conocidas, sin embargo más simple de usar,más elaboradas y con mayor precisión.

El uso de bc

Una forma fantástica de hacer cálculos en Shell – usada normalmente cuando la expresiónaritmética es más compleja, o cuando es necesario trabajar con cifras decimales – es usar lainstrucción de calculo del UNIX/LINUX. El bc. Mira como:

Ejemplo:

$ echo "(2 + 3) * 5" | bc # Paréntesis usados para dar preferencia 25

Para trabajar con números reales (números no necesariamente enteros), especifique la precisión(cantidad de decimales) con la opción scale del comando bc. Así veamos el penúltimo ejemplo:

$ echo "scale=2; 7*5/3" | bc 11.66

Otros ejemplos:

$ echo "scale=3; 33.333*3" | bc 99.999 $ num=5 $ echo "scale=2; ((3 + 2) * $num + 4) / 3" | bc 9.66

Obviamente todos los ejemplos de arriba en el caso de linux, podrían (y deberían) ser escritosusando Here Strings. Veamos los últimos como quedarían:

$ bc <<< "scale=3; 33.333*3" 99.999 $ num=5 $ bc <<< "scale=2; ((3 + 2) * $num + 4) / 3" 9.66

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 107 / 123

Page 108: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Una vez apareció en la lista (excelente a propósito) de Shell script en Yahoo ( "Lista de ShellScript" ) un persona con la siguiente duda: "yo tengo un archivo cuyos campos están separadospor y el tercero de ellos posee números. Como puedo calcular la suma de todos los números deesta columna del archivo?"

Yo envié la respuesta siguiente:

$ echo $(cut -f3 num | tr '\n' +)0 | bc 20.1

Vamos por partes para entenderlo mejor y primero vamos ver como era el archivo que hice paracomprobarlo:

$ cat num a b 3.2 a z 4.5 w e 9.6 q w 2.8

Como se puede ver, está dentro del patrón del problema, donde yo tengo como tercer camponúmeros reales. A ver lo que haría la primera parte de la línea de comandos, donde yo transformolos caracteres (new-line) en un señal de más (+):

$ cut -f3 num | tr '\n' + 3.2+4.5+9.6+2.8+

Si yo mandase de esa manera hacia el bc, él me devolvería un error por causa de aquel signo demás (+) suelto al final del texto. Mi solución fue poner un cero al final, pues sumando cero elresultado no se alterará. Veamos entonces como quedó:

$ echo $(cut -f3 num | tr -s '\n' +)0 3.2+4.5+9.6+2.8+0

Eso es lo que se acostumbra llamar one-liner, esto es, códigos que serían complicados en otroslenguajes (normalmente sería necesario crear contadores y hacer uno loop de lectura sumando eltercer campo al contador) y en Shell son escritos en una única línea.

Hay también gente que llama eso de método KISS, que es el acrónimo de Keep It Simple Stupid.

Pero el uso potencial de esta calculadora no se acaba ahí, existen diversas facilidadesproporcionadas por ella. Veamos sólo este ejemplo:

$ echo "obase=16; 11579594" | bc B0B0CA? $ echo "ibase=16; B0B0CA?" | bc # B, zero, B,zero, C, e A 11579594

En estos ejemplos vimos como hacer cambios de base de numeración con el uso del bc. En laprimera ponemos la base de salida (obase) como 16 (hexadecimal) y en la segunda, dijimos que labase de la entrada (ibase) era 10 (decimal).

Otras formas de trabajar con enteros

Otra forma mucho mejor de hacer cálculos es usar la notación $((exp aritmética)). Es buenoestan atento, sin embargo, al hecho de que esta sintaxis no es universal. Bourne Shell (sh), porejemplo, no la reconoce.

Ejemplo:

Usando el mismo ejemplo que ya habíamos usado:

$ echo $(((2+3)*5)) # Los paréntesis mas internos priorizaran o 2+3 25

Fíjate ahora en esta locura:

$ tres=3 $ echo $(((2+tres)*5)) # Variable tres no precedida por $ 25 $ echo $(((2+$tres)*5)) #Variable tres precedida por $ 25

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 108 / 123

Page 109: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Ei!! No es el signo ($) que la precede lo que caracteriza una variable? Si, pero e todos los saboresUNIX que probé, solo bash o ksh, las dos formas producen una buena aritmética.

Presta atención a esta secuencia:

$ unset i # $i mooorreu! $ echo $((i++)) 0 $ echo $i 1 $ echo $((++i)) 2 $ echo $i 2

Fíjate que a pesar que la variable no esta definida, pues fue hecho uno unset en ella, ninguno delos comandos dió error, porque, como estamos usando construcciones aritméticas, siempre queuna variable no existe, es inicializada con cero (0).

Fíjate que el i++ produjo cero (0). Esto ocurre porque este tipo de construcción se llama pos-incremento, esto es, primeramente el comando es ejecutado y sólo entonces la variable esincrementada. En el caso del ++i, fue hecha un pre-incremento: primero incrementó y solo despuesel comando fue ejecutado.

También son válidos:

$ echo $((i+=3)) 5 $ echo $i 5 $ echo $((i*=3)) 15 $ echo $i 15 $ echo $((i%=2)) 1 $ echo $i 1

Estas tres operaciones serian lo mismo que:

i=$((i+3)) i=$((i*3)) i=$((i%2))

Y esto seria válido para todos los operadores aritméticos, lo que en resumen produciría lasiguiente tabla:

Expansión Aritmética

|| Or lógico

Expresión Resultado id++ id-- pós-incremento y pós-decremento de variables++id -–id pré-incremento y pré-decremento de variables

** exponenciación* / % multiplicación, división, resto de la división+ - adición, sustracción

<= >= < > comparación== != igualdad, desigualdad&& And lógico

Pero el máximo exponente de esta forma de construcción con doble paréntesis es la siguiente:

$ echo $var 50 $ var=$((var>40 ? var-40 : var+40)) $ echo $var 10 $ var=$((var>40 ? var-40 :var+40)) $ echo $var 50

Este tipo de construcción debe ser usado de la siguiente forma: en caso que la variable var seamayor que 40 (var>40), entonces (?) hace var igual a var menos 40 (var-40), si no (:) hacer varigual a var más 40 (var+40). Lo que quiero decir es que los caracteres punto-de-interrogación (?) ydos-puntos (:) hacen el papel de "entonces" y "si no", sirviendo así para montar una operaciónaritmética condicional.

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 109 / 123

Page 110: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Al igual que usamos la expresión $((...)) para hacer operaciones aritméticas, tambiénpodríamos usar la intrínseca (built-in) let o construcciónes del tipo $[...].

Los operadores son los mismos para estas tres formas de construcción, lo que varía un poco es laoperación aritmética condicional con el uso del let. Veamos como sería:

$ echo $var 50 $ let var='var>40 ? var-40 : var+40' $ echo $var 10 $ let var='var>40 ? var-40 :var+40' $ echo $var 50

Baseando

Si quieres trabajar con bases diferentes de la decimal, basta con usar el formato:

base#numero

Donde base es un número decimal entre 2 y 64 en nombre del sistema de numeración, y numero esun número en el sistema numérico definido por base. Si base# fuese omitida, entonces 10 seasume por defecto default. Los guarismos mayores que 9 son representados por letrasminúsculas, mayúsculas, @ y _, en esta orden.

Si base fuese menor o igual a 36, mayúsculas o minúsculas pueden ser usadas indiferentementepara definir guarismos mayores que 9 (no está mal escrito, los guarismos del sistemahexadecimal, por ejemplo, varían entre 0 (cero) y F). Veamos como funciona:

$ echo $[2#11] 3 $ echo $((16#a)) 10 $ echo $((16#A)) 10 $ echo $((2#11 + 16#a)) 13 $ echo$[64#a] 10 $ echo $[64#A] 36 $ echo $((64#@)) 62 $ echo $((64#_)) 63

En estos ejemplos usé las notaciones $((...)) y $[...] indistintamente, para demostrar queambas funcionan.

Funciona también un cambio automático para la base decimal, si estas usando la convenciónnumérica de C, esto es, en 0xNN, el NN será tratado como un hexadecimal y en 0NN, el NN será vistocomo uno octal. Vea el ejemplo:

Ejemplo

$ echo $((10)) # decimal 10 $ echo $((010)) # octal 8 $ echo $((0x10)) # hexadecimal 16 $ echo$((10+010+0x10)) # Decimal + octal + hexadecimal 64

Ah, se me olvidaba! Las expresiones aritméticas con los formatos $((...)), $[...] y con elmando let usan los mismos operadores usados en la instrucción expr, además de los operadoresunários (++, --, +=, *=, ...) y condicionales que acabamos de ver.

Pruebas usando expresiones regularesEn las Conversaciones de Bar 004, lo comentamos todo sobre comandos condicionales, perofaltó uno que no existía a aquella época. En esta misma Conversación de Bar, en la sección Ytoma de test llegamos a hablar de una construcción del tipo:

[[ Expresión ]] && cmd

Donde el comando cmd será ejecutado en el caso que la expresión condicional Expressión seaverdadera. Dije que aunque Expresión podría ser definida de acuerdo con las reglas deGeneración de Nombre de Archivos (File Name Generation). A partir del bash versión 3, fueincorporado a esta forma de test un operador representado por =~, cuya finalidad es hacercomparaciones con Expresiones Regulares.

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 110 / 123

Page 111: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Ejemplo:

$ echo $BASH_VERSION # Asumiendo que la versión de Bash es igual o superior a la 3.0.03.2.17(15)-release $ Cargo=Senador $ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]] && echo Épolítico É político $ Cargo=Senadora $ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]] && echo Épolítico É político $ Cargo=Diretor $ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]] && echo Épolítico $

Vamos desmenuzar la Expresión Regular ^(Governa|Sena|Verea)dora?$: esta acepta todo lo queempieza (^) por Governa, o (|) Sena, o (|) Verea, seguido de dor y seguido de una a opcional (?). Elsigno ($) sirve para marcar el fin de la expresión. En otras palabras esta Expresión Regular seraverdadera si recibe una de las siguientes palabras "Governador, Senador, Vereador,Governadora, Senadora e Vereadora". (En este ejemplo y su explicación hemos omitido latraducción de las constantes para hacer mas comprensible la expresión)

Coloreando la pantallaComo usted ya había visto en la Conversación de Bar 007, el comando tput sirve para hacer casitodo lo referente la formatación de la pantalla, pero lo que no dije es que con él también se puedenusar colores frontales (de los carácteres) y de fondo. Existen también otras formas de hacer lomismo, creo sin embargo, que la que veremos ahora, es más intuitiva (o menos “desintuitiva”). Latabla siguiente muestra los comandos para especificar los patrones de colores frontales(foreground) o de fondo (background):

Obteniendo colores con el comando tput

tput setab n Especifica n como color de fondo (background)

Comando Efecto tput setaf n Especifica n como color frontal (foreground)

Bien, ahora ya sabes como especificar la combinación de colores, pero todavía no sabes loscolores. La tabla siguiente muestra los valores que la n (de la tabla anterior) debe asumir paracada color:

Valores de los colores en el comando tput

7 Gris

Valor Color

0 Negro

1 Rojo

2 Verde

3 Marron

4 Azul

5 Purpura

6 Cian

En este punto ya puedes empezar a jugar con los colores.

- Pero caramba, todavía son mucho pocos!

- Y, tienes toda la razón... El problema es que todavía no te dije que si pones el terminal en modo

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 111 / 123

Page 112: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

de énfasis (tput bold), estos colores generan otros ocho. Vamos a mostar entonces la tabladefinitiva de los colores:

Valores de los colores con el comando tput

7 Gris claro Blanco

Valor Color Color despues de tput bold

0 Negro Gris oscuro

1 Rojo Rojo claro

2 Verde Verde claro

3 Marron Amarillo

4 Azul Azul Brillante

5 Púrpura Rosa

6 Ciano Ciano claro

Ejemplo

Como ejemplo, veamos un script que cambiara el color de la pantalla de acuerdo a tuspreferencias.

$ cat mudacor.sh #!/bin/bash tput sgr0 clear

# Cargando los 8 colores básicos para un vector Colores=(Negro Rojo Verde Marron Azul PúrpuraCian "Gris claro")

# Listando el menu de colores echo " Opc Cor = ===" # La siguiente linia significa: para iempezando desde 1; #+ cuando i es menor o igual al tamaño del vector Colores; #+ incremente elvalor de i de 1 en 1 for ((i=1; i<=${#Cores[@]}; i++)) { printf "%02d %s\n" $i "${Cores[i-1]}" }

CL= until [[ $CL == 0[1-8] || $CL == [1-8] ]] do read -p " Escoje el colorr de la letra: " CL done

# Para los que tienen un bash a partir de la version 3.2 #+ el test do until de arriba podria hacerse#+ usando Expresiones Regulares. Veamos como: #+ until [[ $CL =~ 0?[1-8] ]] #+ do #+ read -p "#+ Escoje el color de la letra: " CL #+ done

CF= until [[ $CF == 0[1-8] || $CF == [1-8] ]] do read -p " Escoje el color de Fondo: " CF done

let CL-- ; let CF-- # Porque los colores varian de cero a siete tput setaf $CL tput setab $CF clear

Ganando la partida con más comodinesEstaba leyendo mis correos electrónicos cuando recibo uno de Tiago enviado a la lista de ShellScript (ya hablé de la lista y de Tiago en el Rotatório Peczenyj). Aquí va el contenido del correoelectrónico:

No sé si es conocido de todos pero el shell posee, ademas del globbing normal (la expansión *, ?y [la-z] de nombres de archivos y directorios), un globbing extendido.

Creo que, en algunos casos, podria ser MUY util, eliminando uno pipe por un grep por ejemplo.

Estos son:

?(patron)

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 112 / 123

Page 113: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Debe coincidir cero o una ocurrencia de un determinado patron

*(patron)

Debe coincidir cero o mas ocurrencias de un determinado patron

+(patron)

Debe coincidir una o mas ocurrencias de un determinado patron

@(patron)

Debe coincidir con exactamente una ocurrencia de un determinado patron

!(patron)

Debe coincidir con cualquier cosa, excepto con patron

Para poder utilizár las es necesario ejecutar shopt conforme al ejemplo siguiente:

$ shopt -s extglob $ ls file filename filenamename fileutils $ ls file?(name) file filename $ lsfile*(name) file filename filenamename $ ls file+(name) filename filenamename $ ls file@(name)filename $ ls file!(name) # divertido esse file filenamename fileutils $ ls file+(name|utils) filenamefilenamename fileutils $ ls file@(name|utils) # "lembra" um {name,utils} filename fileutils

Usando el awk para buscar por equivalenciasAhí va una más que Tiago mandó a la lista de Shell Script de Yahoo (ya hablé de la lista y de Tiagoen el Rotatório Peczenyj y en Ganando la partida con mas comodines)

Quién no pasó ya por ello: Buscar una palabra, sin embargo una letra acentuada, o no, estorbó labúsqueda?

No descubrí como hacer que el grep o el sed acepten algo semejante, pero el gawk acepta clasesde equivalencia!

Mejor explicar con un ejemplo, donde voy listar el número de la línea y la ocurrencia encontrada:

$ cat dados éco eco èco êco ëco eço $ awk '/^eco/{print NR,$1}' dados 2 eco $ awk'/^e[[=c=]]o/{print NR,$1}' dados 2 eco 6 eço $ awk '/^[[=e=]]co/{print NR,$1}' dados 1 éco 2 eco 3èco 4 êco 5 ëco

Es decir, usar [=X=] permite que la expresión encuentre la letra X estando acentuada o no (essensible a la localización corriente!).

La sintaxis es parecida con la de las clases POSIX, cambiando los dos-puntos (:) antes ydespués de la clase por señales de igual (=).

Lo creí curioso y debe servir para algún caso semejante al descrito.

find – Buscando archivo por característicasSi estas como el Sr. Magoo, buscando en vano un archivo, usa el mando find que sirve parabuscar archivos no sólo por el nombre, sino que ademas busca por diversas características. Susintaxis es la siguiente:

find [camino ...] expresión [acción]

Parametros:

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 113 / 123

Page 114: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

camino Path del directorio a partir del cual (porque es recursivo, siempre intentará entrar en lossubdirectórios "colgados" de este) irá buscando en los archivos;expresión Define los critérios de busqueda. Puede ser una combinación entre vários tipos debusqueda;acción Define que acción ejecutar con los archivos que coincidan con los critérios debusqueda definidos por expresión.

los principales critérios de busqueda definidos por expresión son:

-name Busca archivos que tengan el nombre especificado. Aquí pueden ser usadosmetacaracteres o carácteres comodines, sin embargo estos carácteres deberánestar entre comillas, apóstrofos o inmediatamente precedidos por una contrabarra,eso se debe a que quien tienen que expandir los comodines en el find. Si fuese elShell que los expandiese, esto solo se haria con respecto al directorio corriente, loque echaría por tierra la característica recursiva del find;

-user Busca archivos que tengan al usuario como propietario;-group Busca archivos que tengan al grupo como propietario;-type c Busca archivos que tengan el tipo c, correspondiente a la letra del tipo de archivo.

Los tipos aceptados están en la siguiente tabla:Valores de c Tipo de archivo buscado

b Archivo especial accedido por el bloquec Archivo especial accedido por el caracterd Directóriop Named pipe (FIFO)f Archivo normall Link simbólicos Socket

-size ±n[unid]

Busca archivos que usan mas (+n) de n unidades unid de espacio o menos (-n)de n unidades unid de espacio.

Unidades Valorb Bloque de 512 bytes (valor default)c Caracteresk Kilobytes (1024 bytes)w Palabras (2 bytes)

-atime ±d Busca archivos a los que se accedio hace mas (+d) de d dias o menos (-d) de ddias;

-ctime ±d Busca archivos cuyo status cambio hace mas (+d) de d dias o menos (-d) de ddias;

-mtime ±d Busca archivos cuyos datos fueron modificados hace mas (+d) de d dias o menos(-d) de d dias;

Para usar mas de un critério de busqueda, haz: expresión1 expresión2 o expresión1 –a expresión2 para atender a los criterios especificados por expresión1 y expresión2; expresión1 –o expresión2 para atender a los criterios especificados por expresión1 o expresión2.

Las principales acciones definidas para acción son:

-print Esta opción hace que los archivos encontrados sean mostrados en la pantalla.Esta es la opción default en el Linux. En los otros sabores Unix que conozco, si

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 114 / 123

Page 115: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

no se especificase ninguna acción, ocurrirá un error;-exec cmd {} \;Ejecuta el comando cmd. El objetivo del comando es considerado finalizado

cuando se encuentra un punto-y-coma (;). La cadena {} es substituida por elnombre de cada archivo que satisface al criterio de investigación y la línea asíformada es ejecutada. tal y como dijimos para la opción –name, el punto-y-coma(;) debe ser precedido por una contrabarra (\), o debe estar entre comillas oapóstrofos;

-ok cmd {} \; Lo mismo que el anterior sin embargo pregunta si se puede ejecutar lainstrucción cmd sobre cada archivo que atiende al criterio de busqueda

-printf formato

Permite que se elija los campos que serán listados y formatea la salida deacuerdo con lo especificado en formato.

Ejemplos:

Para listar en pantalla (-print) todos los archivos, a partir del diretório actúal, terminados por .sh,haz:

$ find . -name \*.sh Acción no especificada –print el default ./undelete.sh ./ntod.sh estos cuatroprimeros archivos fueron ./dton.sh encontrados en el directório actual. ./graph.sh ./tstsh/cotafs.sh./tstsh/data.sh Estos cuatro fueron encontrados en el ./tstsh/velha.sh directório tstsh, bajo eldirectório corrente ./tstsh/charascii.sh

Necesito obtener espacio en un determinado file system con mucha urgencia, entonces voy aborrar los archivos con más de un megabyte y cuyo último acceso fue hay más de 60 días. Paraeso, voy a este file system y hago:

$ find . –type f –size +1000000c –atime +60 –exec rm {} \;

Observa que en el ejemplo anterior use tres criterios de búsqueda, a saber:

-type f Todos los archivos regulares (normales)-size +1000000c Tamaño mayor de 1000000 de caracteres (+1000000c)-atime +60 Último acesso hace mas de 60 (+60) dias.

Observa que entre estos tres criterios use el conector y, esto es, archivos regulares y mayores que1MByte y sin acceso hace más de 60 días.

Para listar todos los archivos del disco terminados por .sh o .txt, haría:

$ find / -name \*.sh –o –name \*.txt –print

En este ejemplo debemos resaltar además de las contrabarras (\) antes de los asteriscos (*), eluso del –o para una u otra extensión y que el directorio inicial era el raíz (/); siendo así, que estabúsqueda se hizo en todo el disco (lo que frecuentemente es bastante lento).

Con el printf es posible formatear la salida del comando find y especificar los dados deseados.El formateo del printf es muy semejante a la del mismo comando en el lenguaje C e interpretacaracteres de formateo precedidos por un símbolo de porcentaje (%). Veamos sus efectos sobreel formateo:

Caracter Significado%f Nombree del archivo (el path completo no aparece)%F Indica a que tipo de file system o archivo pertence%g Grupo al cual el archivo pertence%G Grupo al cual el archivo pertence (GID- Numérico)

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 115 / 123

Page 116: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

%U Número del usuário (UID) propietario del archivo

Grupo al cual el archivo pertence (GID- Numérico)%h Path completo del archivo (todo menos el nombre)%i Número del inode del archivo (en decimal)%m Permisos del archivo (en octal)%p Nombre del archivo%s Tamaño del archivo%u Nombre del usuário (username) propietario del archivo

También es posible formatear fechas y horas obedeciendo las tablas siguientes:

Carácter Significado%a Fecha del último acceso%c Fecha de creación%t Fecha de Modificación

Los tres caracteres anteriores producen una fecha semejante a al del comando date.

Veamos un ejemplo:

$ find . -name ".b*" -printf '%t %p\n' Mon Nov 29 11:18:51 2004 ./.bash_logout Tue Nov 1 09:44:162005 ./.bash_profile Tue Nov 1 09:45:28 2005 ./.bashrc Fri Dec 23 20:32:31 2005 ./.bash_history

En ese ejemplo, el %p fue el responsable por poner los nombres de los archivos. En caso de seromitido, solamente las fechas serían listadas. Observe que aunque al final se puso una /n. Sin élno habría salto de línea y la lista anterior sería un gran berenjenal.

Esas fechas también pueden ser formateadas, para eso basta pasar las letras de la tabla anteriora mayúsculas (%La, %C y %T) y usar uno de los formateadores de las dos tablas siguientes:

Tabla de formatación de tiempo

Z Huso horário (en mi maravillosa ciudad BRST)

Carácter Significado H Hora (00..23)I Hora (01..12)k Hora (0..23)l Hora (1..12)M Minuto (00..59)p AM or PMr Horário de 12 horas (hh:mm:ss) seguido de AM o PMS Segundos (00 ... 61)T Horário de 24-horas (hh:mm:ss)

Tabla de formatación de fechas

Carácter Significado

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 116 / 123

Page 117: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

Y Año con 4 dígitos

a Dia de la semana abreviado (Dom...Sab)A Dia de la semana completo (Domingo...Sábado)b Nombre del mes abreviado (Jan...Dez)B Dia del mes completo (Enero...Diciembre)c Fecha y hora completa (Fri Dec 23 15:21:41 2005)d Dia del mes (01...31)D Fecha no formateada mm/dd/aah Idéntico a bj Dia secuencial del año (001…366)m Mes (01...12)U Semana secuencial del año. Domingo como 1º dia de la semana (00...53)w Dia secuencial de la semana (0..6)W Semana secuencial del año. Lunes como 1º dia de la semana (00...53)x Representación de la fecha en el formato del país (definido por $LC_ALL)y Año con 2 dígitos (00...99)

Para mejorar la situación, veamos unos ejemplos; sin embargo, veamos primero cuáles son losarchivos del directorio corriente que empiezan por .b:

$ ls -la .b* -rw------- 1 d276707 ssup 21419 Dec 26 17:35 .bash_history -rw-r--r-- 1 d276707 ssup24 Nov 29 2004 .bash_logout -rw-r--r-- 1 d276707 ssup 194 Nov 1 09:44 .bash_profile -rw-r--r-- 1d276707 ssup 142 Nov 1 09:45 .bashrc

Para listar esos archivos en orden de tamaño, podemos hacer:

$ find . -name ".b*" -printf '%s\t%p\n' | sort -n 24 ./.bash_logout 142 ./.bashrc 194 ./.bash_profile21419 ./.bash_history

En el ejemplo que acabamos de ver, el \t fue substituido por un a la salida de forma que la listafuera más legible. Para listar los mismos archivos clasificados por fecha y hora de la últimaalteración:

$ find . -name ".b*" -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sort 2004-11-29 11:18:51./.bash_logout 2005-11-01 09:44:16 ./.bash_profile 2005-11-01 09:45:28 ./.bashrc 2005-12-2617:35:13 ./.bash_history

Algunas implementaciones de Bash 4.0Aquí en este capítulo presentaré los que es nuevo a partir de Bash 4.0. Lo que ya existia, y que fuémejorado a partir de esta versión de Bash (como las ampliaciones en los parámetros), esta siendopublicado en la sección correspondiente.

Coprocessos

A partir de la versión 4.0, Bash incorporo el comando coproc. Este nuevo intrínseco (builtin)permite dos procesos asincronicos se comuniquen y do procesos asíncronos se comuniquen yinteractuen. Como cita Chet Ramey en Bash FAQ, ver. 4.01:

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 117 / 123

Page 118: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

"Hay una nueva palabra reservada, coproc, que especifica un coprocesso: un comando asíncronoque es ejecutado con 2 pipes conectados al Shell creador. coproc puede recibir nombre. Losdescriptores de archivos de entrada y salida y el PID del coproceso estan disponibles para el Shellcreador en variables con nombres específicos del coproc."

George Dimitriu explica:

"El coproc es una facilidad usada en la substitución de procesos que ahora estan publicamentedisponibles."

La comprobación de lo que dice Dimitriu no es complicada, quieres ver? Vea la sustitución deprocesos a continuación:

$ cat <(echo xxx; sleep 3; echo yyy; sleep 3)

Vió?! El comando cat no esperó por la conclusión de los comandos entre paréntesis, pero fueejecutado al final de cada una de ellos. Eso aconteció porque se estableció un pipetemporal/dinámico y los comandos que estaban siendo ejecutados, le mandaban sus salidas, quea su vez le mandaba para la entrada de cat.

Eso significa que los comandos de esta sustitución de procesos giraran paralelos, sincronizandosolamente en las salidas de los echo con entrada de cat.

La sintaxis de un coprocesso es:

coproc [nombre] cmd redirecionamientos

Eso creará un coprocesso llamdo nombre. Si el nombre es informado, cmd deberá ser un comandocompuesto. En caso contrario (en el caso del nombre no será informado), cmd podrá ser uncomando simple o compuesto.

Cuando un coproc es ejecutado, el crea un vector como el mismo nombre nombre en el Shellcreador. La salida padron es unida con un pipe a un descriptor de archivo asociado a la variable${nombre[0]} la entrada en el padron del Shell padre (recuerda que la entrada padrón de unproceso es siempre asociada por default al descriptor cero). De esta forma, la entrada del coproces unida a la salida padrón del script, por un pipe, a un descriptor de archivos llamado${nombre[1]}. Así, simplificando, vemos que el script mandará datos para o coproc por la variablel${nombre[0]}, y recibirá su salida en ${nombre[1]}. Note que estos pipes serán creados antes delos direccionamientos especificados por el comando, creados antes de los redirecionamientosespecificados por el comando, ya que ellos serán las entradas y salidas del coprocesso.

A partir de aquí, voy a detallar rapidamente unos estudios que hice. Esto contiene un poco dedivagaciones y mucha teoría. Si tú quieres saltar para después de estos ls's, no perderás nada,pero si acompañas, puede ser bueno para la comprensión del mecanismo del coproc.

Depués de colocar un coproc activo, si él esta asociado a un descriptor de un archivo, vamos a verlo que tiene activo en el directorio correspondiente:

$ ls -l /dev/fd lrwxrwxrwx 1 root root 13 2010-01-06 09:31 /dev/fd -> /proc/self/fd

Hummm, es un link para el /proc/self/fd... Lo que será que tiene allá?

$ ls -l /proc/self/fd total 0 lrwx------ 1 julio julio 64 2010-01-06 16:03 0 -> /dev/pts/0 lrwx------ 1 juliojulio 64 2010-01-06 16:03 1 -> /dev/pts/0 lrwx------ 1 julio julio 64 2010-01-06 16:03 2 -> /dev/pts/0lr-x------ 1 julio julio 64 2010-01-06 16:03 3 -> /proc/3127/fd

Opa, que el 0, 1 y 2 apuntaba para /dev/pts/0 yo ya sabía, pues son las entradas padrón, salida

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 118 / 123

Page 119: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

padrón y salida de errores padrón apuntando para el pseudo terminal corriente, pero que será esemaldito device 3?

Veamos:

$ ls -l /proc/$$/fd # $$ Es el PID del Shell corriente total 0 lr-x------ 1 julio julio 64 2010-01-06 09:310 -> /dev/pts/0 lrwx------ 1 julio julio 64 2010-01-06 09:31 1 -> /dev/pts/0 lrwx------ 1 julio julio 642010-01-06 09:31 2 -> /dev/pts/0 lrwx------ 1 julio julio 64 2010-01-06 16:07 255 -> /dev/pts/0 l-wx------ 1 julio julio 64 2010-01-06 16:07 54 -> pipe:[168521] l-wx------ 1 julio julio 64 2010-01-06 16:0756 -> pipe:[124461] l-wx------ 1 julio julio 64 2010-01-06 16:07 58 -> pipe:[122927] lr-x------ 1 juliojulio 64 2010-01-06 16:07 59 -> pipe:[168520] l-wx------ 1 julio julio 64 2010-01-06 16:07 60 ->pipe:[121302] lr-x------ 1 julio julio 64 2010-01-06 16:07 61 -> pipe:[124460] lr-x------ 1 julio julio 642010-01-06 16:07 62 -> pipe:[122926] lr-x------ 1 julio julio 64 2010-01-06 16:07 63 ->pipe:[121301]

Opa, ahí están los links aputando para los pipes. Esa cantidad de archivos de pipe que fué listado,debe ser porque estaba testeando exaustivamente esa nueva facilidad del Bash.

Para terminar esta teoria aburrida, falta decir que el PID del Shell generado para interpretar ocoproc puede ser obtenido en la variable $nombre_PID es el comando wait puede ser usado paraesperar por el fin del coprocesso. El código de retorno del coprocesso ($?) es el mismo de cmd.

Ejemplo:

Vamos comenzar con los más simples: un ejemplo sin nombre y directo en el prompt:

$ coproc while read Entra # coproc activo > do > echo -=-=- $Entra -=-=- > done [2] 3030 $ echoHola >&${COPROC[1]} # Manda Hola para el pipe de salida $ read -u ${COPROC[0]} Sale # Leedel pipe de entrada $ echo $Sale -=-=- Hola -=-=- $ kill $COPROC_PID # Eso no puede serolvidado...

Como puedes ver, el vector COPROC, está asociado a dos pipes; el ${COPROC[1]} que contiene ladirección del pipe de salida, es por eso la salida del echo está redireccionada para él y${COPROC[0]} que contiene la dirección del pipe de entrada, y por eso usamos la opción -u delread que lee datos a partir de un descriptor de archivo definido, al revés de la entrada padrón.

Como él coprocesso utilizaba la sintaxis sin nombre, el padrón del nombre del vector es COPROC.

Solo una más "pequeña teoria" aburrida:

$ echo ${COPROC[@]} # Lista todos los elementos del vector 59 54

Como vistes ${COPROC[0]} estaba usando el pipe apuntado por /proc/$$/fd/59 y ${COPROC[1]}usaba /proc/$$/fd/54. Ahora paramos la teoría de verdad! Vamos a usar nombre en este mismoejemplo, para ver que poco cambia

$ coproc test { > while read Entra > do > echo -=-=- $Entra -=-=- > done > } [6] 3192 $ echo Hola>&${test[1]} $ read -u ${test[0]} Salida $ echo $Salida -=-=- Hola -=-=- $ kill $test_PID

En éste momento, es bueno mostra una cosa interesante: Cuales son los procesos en ejecución?

$ ps # Solamente en el Bash en ejecución PID TTY TIME CMD 1900 pts/0 00:00:01 bash 2882pts/0 00:00:00 ps

Vamos ejecutar 2 coprocessos simultáneos:

$ coproc nombre1 { # Coprocesso nombre1 > while read x > do > echo $x > done; } [1] 2883 $coproc nombre2 { # Coprocesso nombre2 > while read y > do > echo $y > done; } bash: aviso:

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 119 / 123

Page 120: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

execute_coproc: coproc [2883:nombre1] still exists [2] 2884

Jiiii! Me parece que hubo un problema! Pero será que si hubo un problema? Observe que ademásdel PID 2883 del nombre1, el tambien me devolvió el PID 2884, que debe ser del nombre2. Vamos aver lo que está pasando:

$ ps PID TTY TIME CMD 1900 pts/0 00:00:01 bash Ese yá existia 2883 pts/0 00:00:00 bash Eseestá ejecutando nombre1 2884 pts/0 00:00:00 bash Ese está ejecutando nombre2 2885 pts/000:00:00 ps

Parece que fué solo un aviso, pues los dos PIDs informados cuando iniciamos los doscoprocessos, estan activos. Entonces vamos a testear esos 2 tipos:

$ echo xxxxxxxxx >&${nombre1[1]} # Mandando cadena para nombre1 $ echo yyyyyyyyy>&${nombre2[1]} # Mandando cadena para nombre2 $ read -u ${nombre1[0]} Recibe $ echo$Recibe xxxxxxxxx $ read -u ${nombre2[0]} Recibe $ echo $Recibe yyyyyyyyy $ kill $nombre1_PID$ kill $nombre2_PID

Vectores asociativos

A partir de Bash 4.0, pasó a existir el vector asociativo. Llamado vector asociativo, aquellos cuyosindices son alfabéticos. Las reglas que valen para los vectores enteros, valen también para losasociativos, por eso antes de valorar estos últimos, es obligatorio declararlos.

Ejemplo:

$ declare -A Animales # Obligatorio para el vector asociativo $ Animales[caballo]=doméstico $Animales[zebra]=salvaje $ Animales[gato]=doméstico % $ Animales[tigre]=salvaje

Es imposible generar todos los elementos de una sola vez, como en los vectores enteros. Si fueraasí, la sintaxis no funciona:

Animales=([caballo]=doméstico [zebra]=salvaje [gato]=doméstico [tigre]=salvaje)

$ echo ${Animales[@]} doméstico salvaje doméstico salvaje $ echo ${!Animales[@]} gato zebracaballo tigre

Observe que los valores no están ordenados, quedan almazenados en el orden que son creados, adiferencia de los vectores enteros que quedan ordenados numéricamente.

Supongo que ese vector tuviese centenas de elementos, para listar separadamente losdomésticos de los salvajes, podriamos hacer un script asi:

$ cat animal.sh #!/bin/bash # Separa animales salvajes de los domésticos declare -A AnimalesAnimales[caballo]=doméstico # Creando vector para el test Animales[zebra]=salvaje # Creandovector para el test Animales[gato]=doméstico # Creando vector para el test Animales[tigre]=salvaje# Creando vector para el test Animales[oso pardo]=salvaje # Creando vector para el test forAnimal in "${!Animales[@]}" # Recorriendo vector por el indice do if [[ "${Animales[$Animal]}" ==salvaje ]] then Sel=("${Sel[@]}" "$Animal") # Generando vector p/ salvajes else Dom=("${Dom[@]}""$Animal") # Generando vector p/ domésticos fi done # Operador condicional, usado paradescubrir cual #+ El vector tiene mas elementos. Vea detalles en la sección #+ El interpretadoraritmético del Shell Mayor=$[${#Dom[@]}>${#Sel[@]}?${#Dom[@]}:${#Sel[@]}] clear tput bold;printf "%-15s%-15s\n" Domésticos Salvajes; tput sgr0 for ((i=0; i<$Mayor; i++)) { tput cup $[1+i] 0;echo ${Dom[i]} tput cup $[1+i] 14; echo ${Sel[i]} }

Me gustaría llamar tú atención para un detalle: en este script me referí a un elemento de vectorasociativo empleando ${Animales[$Animal]} me referí a un elemento de un vector usando${Sel[i]}. O sea, cuando usamos una variable como indice de un vector entero, no es necesario

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 120 / 123

Page 121: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

agregarle un ($), mientras que en el vector asociativo, el símbolo de ($) es obligatorio.

Leyendo un archivo para un vector

Todavia hablando de Bash 4.0, es que él surge con otra novedad: el comando intrínseco (builtin)mapfile, cuya finalidad es mover un archivo de texto entero dentro de un vector, sim loop osubstitución de comando.

- EPA! Eso debe ser muy rápido!

- Y es. Haga el test y compruebe!

Ejemplo:

$ cat frutas abacate manzana frutilla pera tangerina uva $ mapfile vet < frutas # Mandando frutaspara el vector vet $ echo ${vet[@]} # Listando todos los elementos de vet abacate manzana frutillapera tangerina uva

Obtendriamos un resultado idéntico si hicieramos:

$ vet=($(cat frutas))

Por eso seria mas lento porque la substitución del comando es ejecutada en un subshell Una formade hacer esto que ahora me viene a la cabeza es leer el archivo con la opción -a del comandoread. Vamos a ver como seria el comportamiento de esto:

$ read -a vet < frutas $ echo ${vet[@]} abacate

Como dió para percibir, fué leido solamente el primero registro de las frutas.

Caja baja para alta y viceversa

Usando ampliaciones de parámetro

${parâmetro^}

${parâmetro,}

Essas expansões de parâmetros foram introduzidas a partir do Bash 4.0 e modificam a caixa dasletras do texto que está sendo expandido. Quando usamos circunflexo (^), a expansão é feita paramaiúsculas e quando usamos vírgula (,), a expansão é feita para minúsculas.

Exemplo:

$ Nome="botelho" $ echo ${Nome^} Botelho $ echo ${Nome^^} BOTELHO $ Nome="botelhocarvalho" $ echo ${Nome^} Botelho carvalho # É pena que não fique Botelho Carvalho...

Um fragmento de script que pode facilitar a sua vida:

read -p "Deseja continuar (s/n)? "[[ ${REPLY^} == N ]] && exit

Esta forma evita testarmos se a resposta dada foi um N (maiúsculo) ou um n (minúsculo).

Usando declaratives

Podemos hacer algo parecido, pero de otra manera. Ahora, para que tengamos solamentemayúsculas en una variable tambien podemos declararlas usando la opción -u ( de uppercase =

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 121 / 123

Page 122: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

mayúscula). Vea:

$ declare -u Maiusc

Una vez declaradas, vea este ejemplo

$ read -p "Tecle algo: " Maiusc # La variable Maiusc recibirá lo que sea tecleado Teclee algo:convierte a mayúsculas $ echo $Maiusc CONVIERTE A MAYÚSCULAS

Lo contrario de esto seria si usaramos la opción -l (de lowercase = minúscula). Vea:

$ declare -l Minusc $ read -p "Tecle algo: " Minusc # La variable Minusc recibirá lo que seatecleado Teclee algo: CONVIERTE PARA MINÚSCULAS $ echo $Minusc convierte paraminúsculas

Todo lo que fue dicho sobre estas conversiones, solo es válido despues que declare sea hecho.Observe:

$ var="xxxx" # Voy a atribuir antes de declarar $ declare -u var $ echo $var xxxx # Nada cambio,continua en minúscula $ var="xxxx" # Despues de la declaración $ echo $var XXXX # Ahorafuncionó...

Nuevas implementaciones en el comando case

Bash 4.0 introdujo 2 nuevas facilidades en el comando case. A partir de esta versión, existen 2terminadores mas de bloques además de ;;, que son: ;;& - Cuando un bloque de comandos fueracerrado con este argumento, el programa no saldra del case, pero testeará los proximospadrones; ;& - Este caso, el próximo bloque será ejecutado, sin ni siquiera chequear su padrón.

Ejemplo:

Vea este fragmente de código adaptado de http://tldp.org/LDP/abs/html/bashver4.html:

case "$1" in [[:print:]] ) echo $1 es un caracter imprimible;;& # O terminador ;;& testeará el próximo padrón padrão [[:alnum:]] ) echo $1 es un caracter alfa/numérico;;& [[:alpha:]] ) echo $1 es un caracter alfabético ;;& [[:lower:]] ) echo $1 es una letra minúscula ;;& [[:digit:]] ) echo $1 es un caracter numérico ;& # O terminador ;& ejecutará el próximo bloque... %%%@@@@@ ) echo "************************" ;;# ^^^^^^^^ ... igual con un padrón loco.esac

La ejecución de este código pasando 3 como parámetro, resultaria:

3 es un caracter imprimible 3 es un caracter alfa/numérico 3 es un caracter numérico********************************

Pasando m:

m es un caracter imprimible m es un caracter alfa/numérico m es un caracter alfabéticom es una letra minúscula

Pasando / :

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 122 / 123

Page 123: Conversación de Bar - Parte Ivdo.dyndns.org/so3/bash_libro_so3.pdf · Conversación de Bar - Parte I Diálogo escuchado entre un Linuxer y un empujador de mouse: El Ambiente Linux

/ es un caracter imprimible

Expansión de llaves

Bash 4.0 incorporó dos nuevas formas de hacer la expansión de llaves:

Secuencia numérica con relleno de ceros a la izquierda;Posibilidad de usar un incremento (pasar) en una secuencia numérica.

Ejemplos:

$ echo {0010..0019} # Rellenando con 2 ceros a la izquierda 0010 0011 0012 0013 0014 00150016 0017 0018 0019 $ echo {05..15} # Rellenando con ceros a la izquierda 05 06 07 08 09 10 1112 13 14 15 $ echo {-5..19..3} # Incrementando de 3 en 3 (Passo 3) -5 -2 1 4 7 10 13 16 19 $ echo{000..100..10} # Rellenando con ceros y paso 10 000 010 020 030 040 050 060 070 080 090 100

Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza, lo único que tienes quehacer es mandarme un e-mail a [email protected] para informarse.

Gracias y hasta la próxima!

-- RandolphChaves - 03 Feb 2012

(CC) 2014 Pelos Frequentadores do Bar do Júlio Neves.Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative CommonsLicense: Atribuição-UsoNãoComercial-PermanênciaDaLicença.

.:TWikiBaracompanhamentos > TWikiBar > Aqui temos um _livro livre_ e completo sobre _Shell_:. 09/02/14

http://wiki.softwarelivre.org/TWikiBar/TWikiBaracompanhamentos 123 / 123