next up previous contents
Next: Archivos de comandos Up: El Sistema Operativo Unix Previous: Editor de línea ed   Índice General

Interface con el usuario

En la interacción con Unix se puede, aparte de hacer las mismas tareas comúnes que en cualquier otro sistema operativo, tener la posibilidad de programar o automatizar tareas de una manera realmente eficiente.

En la sección [*] anterior dejamos pendiente la explicación del programa grep. Vagamente recordamos que se trata de un programa que busca cadenas de texto dentro de un archivo o de varios. Bien, con frecuencia nos enfrentamos a éste problema: necesitamos imprimir o alterar un archivo en particular, del cual no recordamos el nombre, pero si que contenía una palabra o una frase en particular. Digamos que recordamos que se trata de una carta que enviamos a un cliente importante donde usamos la frase: ``es usted un cretino insolente''. La manera pedestre de encontrar el archivo sería revisar todos y cada uno de los archivos contentidos en el subdirectorio cartas del directorio clientes. Pero, y si son quinientas cartas? Bueno, en este caso comprenderemos porque grep es valioso. Basta con dar:

pablo@diva~/cartas/clientes$ grep "es usted un cretino insolente" *
director.Banco.Nacional.25.de.junio.de.1994.txt:si cree usted que
podr\'a salirse con la suya y no pagarnos el adeudo, me tomo la libertad de
informarle que es usted un cretino insolente,
director.regional.Nuevo.Leon.27.junio.1994.txt:y sin
ningun motivo aparente, me espet\'o: el cretino insolente lo ser\'a
usted. Obra en mi poder la prueba

Y vemos como grep, rápidamente, encontró dos documentos con la frase que entre comillamos. La razón del entre comillado, es que grep espera que el primer argumento sea el elemento a buscar y los demás sean los archivos donde buscará, así que con las doble comillas le avisamos al sistema operativo que la frase forma un grupo y que éste deberá ser pasado al programa grep como un sólo argumento.

El segundo argumento, el asterisco, es en realidad un wildcard que le avisa al sistema operativo: toma todos los archivos de éste directorio y pásaselos como argumentos al programa. Así que, en realidad, grep no recibe * como segundo argumento, sino toda la lista de archivos que contiene el directorio. Esta es otra diferencia con respecto a MS-DOS en que, como todo programador sabrá, el programa recibiría en efecto el asterisco y el programa debe saber que hacer con él.

Qué pasaría si ni siquiera sabemos en que subdirectorio se encuentra la carta? Bueno, pues desde el subdirectorio cartas podríamos pedir:

pablo@diva~/cartas$ grep "es usted un cretino insolente" */*
./clientes/director.Banco.Nacional.25.de.junio.de.1994.txt:si
cree usted que podr\'a salirse con la suya y no pagarnos el adeudo, me
tomo la libertad de informarle que es usted un cretino insolente,
./clientes/director.regional.Nuevo.Leon.27.junio.1994.txt:y
sin ningun motivo aparente, me espet\'o: el cretino insolente lo ser\'a
usted. Obra en mi poder la prueba

Puede explicar que ocurre al utilizar "*/*" como último argumento?

Ahora bien, supongamos que conocemos el nombre del archivo, pero no recordamos en que directorio está. Digamos que el nombre del archivo es breve_carta_donde_le_recuerdo_a_mi_tia_Genoveva_que_soy_su_sobrino_favorito.txt. De nuevo sabemos que está en algún lugar del árbol a partir del subdirectorio cartas. De nuevo, grep nos puede ayudar.

pablo@diva~/cartas$ ls -R | grep breve_carta_a_mi_tia
./herencias/breve_carta_donde_le_recuerdo_a_mi_tia_Genoveva_que_soy_su_sobrino_favorito.txt

La opción -R de ls lista recursivamente el contenido de archivos y subdirectorios.

O podríamos utilizar la opción -a de du, que nos lista el espacio ocupado en bloques de todos los archivos:

pablo@diva~/cartas$ du -a | grep breve_carta_a_mi_tia
423     ./herencias/breve_carta_donde_le_recuerdo_a_mi_tia_Genoveva_que_soy_su_sobrino_favorito.txt

Y que además nos informa que el archivo contiene 423 kilobytes.

En realidad no necesitamos recurrir a un pipe para hacer esto. Existe el programa find que se utiliza para encontrar archivos y que además permite ejecutar acciones sobre de ellos. En este caso lo podríamos haber utilizado de la siguiente manera:

pablo@diva~/cartas$ find . -name '*Genoveva*favorito*' -print
./herencias/breve_carta_donde_le_recuerdo_a_mi_tia_Genoveva_que_soy_su_sobrino_favorito.txt

Con el primer argumento `.' le informamos a find que la búsqueda se realizará a partir del directorio actual, con -name le pedimos que haga la búsqueda por nombre, con el tercero le informamos que como parte del nombre aparecen Genoveva y favorito, en ése orden, y con el cuarto le pedimos que cuando lo encuentre, imprima el path donde lo halló. Utilizamos la construcción '*Genoveva*favorito*' por la siguiente razón, con los asteriscos le informamos que antes de Genoveva ocurre cualquier combinación de carácteres, al igual que entre Genoveva y favorito, así como después de éste último. Utilizamos el apóstrofe para ocultar los astericos al shell, que es el programa con el que hemos estado interaccionando todo este tiempo.

Ejercicio: De no ocultar los asteriscos al shell qué ocurriría?

Como hemos visto hasta ahora, Unix nos proveé de varias maneras de hacer las cosas y las construcciones que emplean los pipes nos permiten llevar a cabo tareas tan complejas como se nos ocurra.

Haremos un ejemplo un poco mas complicado que nos revela cúan poderoso puede llegar a ser Unix. En éste caso sabemos que en algún lugar de nuestro directorio existen varios archivos que como parte del nombre tienen la palabra pericles y queremos encontrar en particular aquél que habla de parias nucleares. Como ya sabemos usar find para encontrar archivos que cumplen con un patrón determinado y además sabemos utilizar grep para encontrar frases dentro de archivos, la solución es sencilla.

pablo@diva~$ grep 'parias nucleares' `find . -name '*pericles*' -print`
./textos/poesia/oda_postmoderna_a_pericles.txt:las mujeres
sucias, parias nucleares de \'esta tragedia

Sabemos bien de que se trata lo de grep 'parias nucleares' y también comprendemos que queremos al utilizar find . -name '*pericles*' -print, lo único nuevo en este caso es la construcción tan extraña y la comilla simple que encierra a find y sus argumentos. Sabíamos que el primer argumento a grep es la palabra o frase a buscar dentro del archivo y que los demás argumentos son archivos. También sabíamos que find encuentra archivos que cumplen con una condición determinada. Entonces, por qué no aprovechar ambos para realizar la tarea? Para conseguirlo, le indicamos al shell que el segundo argumento a grep va a ser el resultado del find. La comilla antes y después son precisamente para esto. El shell al encontrar una instrucción encerrada por comilla sencilla, sabe que primero debe de ejecutar esa instrucción y luego el resto del comando. En este caso ejecutará primero el find y el resultado lo utilizará como argumento al grep.

Ahora vamos a hacer un corrector ortográfico para pobres. Lo primero que necesitamos es un diccionario. Para esto, tomamos algunos archivos que sepamos con seguridad que no contienen muchas faltas de ortografía. Los pegamos todos a un archivo temporal con el programa cat:

pablo@diva~$ cat carta.txt entrevistas.virtuales.txt ipt.txt \
pgp.txt wired.txt > /tmp/borrame

Construimos el diccionario:

pablo@diva~$ cat /tmp/borrame | tr -c 'a-zA-Z\341\351\355\363\372\361' ' '| \
tr ' \t' '\n'| sort | uniq > diccionario

Y con esto ya tenemos una lista de palabras, una por renglón, que podemos utilizar para el corrector. Veamos por partes como se construye. Hacemos un cat y un pipe para pasarle el archivo con que vamos a construir el diccionario a un programa llamado tr. Para comprender mejor que es lo que hace tr, veamos primero lo que occurre con la segunda aparición de éste programa en la línea de comandos. La tarea de tr es la de traducir conjuntos de carácteres en conjuntos de carácteres en los ordenes respectivos. Si queremos traducir todo un archivo de minúsculas a mayúsculas, bastaría con usar tr 'a-z' 'A-Z'. Por ejemplo, si tenemos un archivo llamado minusculas y queremos pasar todo su contenido a mayúsculas en el archivo salida.mayusculas, usaríamos:

pablo@diva~$ cat minusculas | tr 'a-z' 'A-Z' > salida.mayusculas

Regresando al ejemplo del corrector ortográfico, para construir la lista de palabras necesitamos que estas ocurran una por línea y además que no aparezcan signos de puntuación, números ni símbolos. Lo que hacemos en el ejemplo con la primera ocurrencia de tr es, precisamente, eliminar todos los carácteres que no sean alfabéticos aprovechando la opción -c que indica que se tome el complemento del primer conjunto, es decir que se traducirán todos los carácteres que no aparezcan en el primer conjunto, que son todos los carácteres distintos de la a a la z minúsculas y mayúsculas y seis números que representan a las vocales acentuadas y a la ñ en el estándar ISO-latin1.

Una vez hecho lo anterior, en la tubería en lugar del archivo original, viaja un archivo donde no hay signos de puntuación, dígitos, símbolos ni fines de línea. Sólo queda una lista de palabras separadas por espacios, y nosotros la necesitamos separadas una por línea, así que recurrimos nuevamente a tr con los conjuntos ' ' y '\n'. En este caso el primer conjunto es ' ', que consiste en un espacio y el segundo es un identificador especial que representa al cambio de línea. Así que ahora en la tubería viaja un archivo que tiene una palabra por línea.

Por último, necesitamos que las palabras estén en orden y que no aparezcan más de una vez. Para esto usamos sort, que se encarga de ordenarlas, y uniq, que deja pasar sólo una ocurrencia de cada palabra.

Estas conversiones las hicimos porque vamos a utilizar una utilería de Unix llamada comm, que compara línea por línea dos archivos ordenados y en la salida escribe en la primera columna lo que aparece exclusivamente en el primer archivo, en la segunda lo que ocurre exclusivamente en el segundo y en la tercera lo que aparece en ambos. Digamos que tenemos dos archivos llamados Juan y Pedro, que representan a dos matemáticos amigos nuestros, y que cada archivo contiene la lista de coches que posee cada uno. Así, si Juan tiene: un Bentley, un Ferrari, un Jaguar, un Masserati, un Peugeut y un Roll Royce, y Pedro tiene: un Barmann Costa, un Jaguar, un Lamborghinni, un Mercedes Benz, un Peugeut y un Porsche; entonces la salida será:

pablo@diva:~$ comm Juan Pedro
                Barmann Costa
Bentley
Ferrari
                                        Jaguar
                Lamborghinni
Masserati
                Mercedes Benz
                                        Peugeut
                Porsche
Roll Royce

Donde vemos que en efecto, ambos coinciden en tener un Peugeut y un Jaguar, aunque naturalmente, en la vida real, tratándose de personas de gran éxito económico como los matemáticos, ésta lista sería demasiado grande para hacer la comparación a mano, lo cual justificaría aún mas el utilizar programas como comm.

Un último detalle, es que como salida del corrector ortográfico queremos que se produzca la lista de palabras que tenemos en el archivo a revisar y que no están en el diccionario, así que sólo necesitamos como salida la tercera columna producida por comm. Esto lo podríamos hacer utilizando otro programa, pero afortunadamente comm considera este caso y como opción se le puede decir que columnas se omiten. Esto se hace sencillamente indicándoselas con el número de columna, en nuestro ejemplo omitiremos las columnas segunda y tercera, es decir, las columnas que listan las palabras en nuestro documento y las del diccionario. Así que la opción que le daremos a comm es -23. Otra aclaración es que comm espera como entrada dos archivos y los que vamos a utilizar son el diccionario que creamos, del mismo nombre, y el archivo que está viajando por la tubería, el cúal no existe como archivo con un nombre asignado, pero ésto tampoco es problema, porque en Unix los archivos creados en base a pipes tienen como nombre alternativo simplemente -. Así que finalmente estamos listos para ejecutar el corrector ortográfico de los pobres:

pablo@diva~$ cat unix.txt | tr -c 'a-zA-Z\341\351\355\363\372\361' ' '| \
tr '\t' '\n'| sort | uniq | comm -23 -diccionario | more

De haber seguido con atención nos daremos cuenta de lo limitado que resulta este corrector por varias razones. La primera es que no nos indica en que lugar del archivo ocurre la palabra, otra es que no hay ninguna garantía de que esa palabra esté mal, simplemente no está entre las que consideramos correctas y la tercera es que no hace el reemplazo adecuado, pero precisamente por eso lo llamamos el corrector ortográfico de los pobres.



Subsecciones
next up previous contents
Next: Archivos de comandos Up: El Sistema Operativo Unix Previous: Editor de línea ed   Índice General
Ismael Olea 2001-04-21