|
FILTROS 3.14 |
|
3.14.1 Introducción
Kem Thompsom y Denis Ritchie cuando crearón Unix a principios de los sesenta tuvieron en mente crear un
sistema operativo que facilitase la labor a los programadores. A la conclusión que llegaron es que la mejor forma es
establecer un conjunto definido de pequeñas herramientas que hagan una determinada labor muy bien. A partir de la unión
de esas herramientas se pueden realizar labores más complejas. Los resultados de unas se conviertan en entradas de otra.
Esa forma de trasmitirse la información se realiza con el uso de la entrada y salida estandar ( pantalla y teclado ).
Gracias a la existencia de las tuberias y redireciones se consiguen esos resultados.
Se puede apreciar en un ejemplo. Si un usuario introduce esta linea:
whogrep son programas distintos separados por la tubería "|" . who nos muestra un listado de
todos los usuarios conectados al ordenador en el momento dado. Su salida habitual sería algo parecido a esto:
$ who
tyfa tty1 Ago 22 13:15
pepe ps/2 Ago 22 14:36
root tty2 Ago 22 10:03
pepe ps/2 Ago 22 14:37
|
Lo que devuelve who se divide en 4 campos separados por tabuladores. Los cuales son nombre de usuario (login),
terminal en el que esta conectado, fecha y hora de inicio de la conexión.
"grep pepe" por su parte busca las lineas de esa salida que contengan la secuencia "pepe".
De esta forma la salida es:
$ who | grep pepe
pepe ps/2 Ago 22 14:36
pepe ps/2 Ago 22 14:37
|
Por ejemplo si ahora el usuario en cambio quiere algo más simple, es decir saber si está o no está conectado mediante
el número de conexiones que tiene en este momento se debe usar la utilidad wc.
wc es contador de letras,palabras y lineas. En este caso solo precisamos saber el número de lineas así que se ha de
utilizar la opción -l
$ who | grep pepe | wc -l
2
|
pepe esta conectado en 2 terminales
Si ahora probamos con antonio
$ who | grep antonio | wc -l
0
|
antonio no esta conectado
3.14.2 grep, egrep, fgrep
En el ejemplo anterior hemos visto básicamente cual es el objetivo de grep.
grep básicamente funciona de esta manera:
$ grep [-opciones] patrón ficheros
Las opciones más comunes son:
-n antepone a cada linea emparejada con su número de línea (útil para buscar algo en un fichero muy largo y saber donde esta especificamente)
-c imprime solo el número de coincidencia encontradas
-v solo se buscan las no coincidencias ( cuando lo que buscamos es lo que no coincide con el patrón )
El patrón es cualquier conjunto de caracteres que se busca. Hay que indicar es que si van acompañados de un espacio
grep confundira el patrón con los ficheros a buscar, eso se soluciona con la ayuda de los caracteres ".
Por ejemplo:
$ grep "Hola mundo" fichero
|
Si buscamos alguna cadena que contenga un comodín , apóstrofes , comillas , redirreciones o barras del tipo "\",
se deberá anteponer una barra "\" para asi indicar de que buscamos esa caracter en si y no la substitución del
comodín , o que iniciamos una cadena de varias palabras.
Esto es una cadena chunga -> *"?<
3.14.3 Sed
sed es una de las herramientas más peculiares de Unix. sed significa Stream Editor (o sea editor de flujo).
Los editores suelen aceptar de forma interactiva las modificaciones que se desean insertar. Sed nos permite
crear pequeños programas "shell scripts" parecidos a los batch de MS-DOS. Sed nos da la capacidad de modificar
de forma automática el contenido de un fichero, permitiendonos crear shell scripts que lo modifiquen "on the fly". Las
capacidades de este editor son muy completas.
Sed suele llamarse de esta forma:
$ sed 'comando-sed' fichero
|
Por ejemplo tengamos un fichero en el que deseamos reemplazar todos las apariciones de "Manolo" por "Fernando".
Manos a la obra:
$ sed 's/Manolo/Fernando/g' archivo
|
Y devuelve por salida estandar las modificaciones. Si se desea conservar el resultado se redigirá con ">"
Los usuarios de vi reconecerán inmediatamente que se trata de un comando típico de vi de busqueda y sustitución.
En realidad los comandos de tipo ":" (los que invocan a ex) se pueden usar con sed.
La estructura de las ordenes sed consiste en indicar primero una cadena (o secuencia de cadenas) sobre las que actuar
y luego el comando. Para indicar una cadena se puede indicar un número, un intervalo de numeros o buscar un patrón.
Los comandos usuales de sed:
a\ añade las lineas siguientes a las lineas afectadas
c\ cambia las lineas afectadas por las lineas siguientes
d borra las lineas afectadas
g hace sustituciones generales, de todos los patrones localizados inserta en lugar de limitarse al primero
i\ inserta las lineas siguientes a las afectadas
p imprime la línea, incluso con la opción -n
q abandonada (quit) cuando se alcanza la línea especificada
r fichero lee un fichero , añadiendo el contenido a la salida
s/uno/dos sustituye la cadena "uno" por "dos"
w fichero copia esa linea a otro fichero
= imprime el número de línea
! comando aplica un comando a dicha línea
Con sed se puede especificar sobre que lineas o conjunto de lineas se aplica el comando:
Borrará la tercera línea del archivo
$ sed '2,4s/e/#/' fichero
|
Substituye el caracter e por # en las lineas del 2 a la 4 inclusive.
También es posible realizar comandos sobre lineas que contengan una determinada cadena haciendo uso, si se desea,
de expresiones regulares.
$ sed '/[Qq]ueen/d' canciones
|
Borra todas las lineas que contengan la cadena "Queen" o "queen".
Con la ayuda de expresiones regulares podemos por ejemplo eliminar las lineas vacias de un fichero.
Aunque esto no borrará las cadenas que contengan espacios. Con esta versión además se consigue ese propósito.
La secuencia ' *' indica que se debe buscar cualquier combinación de cero o mas apariciones del patrón ' '.
$ sed '/InitMenu/a\
> gvim gvim.xpm exec gvim &' .xvwmrc
|
Este ejemplo buscaría una linea que contenga la cadena InitMenu y después de esa le añadiria esa cadena.
Cabe indicar de que awk y sed forman pieza clave de los shell scripts más complejos. Puede resultar realmente
impresionante lo que se puede llegar a hacer sin hacer uso para nada de C o cualquier otro lenguaje compilado.
Cabe destacar que por ejemplo el setup de la distribucion de slakware así como muchos CGI del web son en realidad shell
scripts.
Últimamente el uso de las herramientas de la línea de comandos ha sido algo denostado, achacandole demasiada antiguedad
para los entornos de ventanas que se usan actualmente así como la llegada de lenguaje Perl que pretende ser un
substituto de shell scripting dan a indicar que estas herramientas están condenadas al olvido. En mi experiencia
he podido comprobar que muchas aplicaciones ( incluso un pequeño gestor de base de datos ) es cuestión de pocas
líneas de código en shell script.
Es aquí donde awk junto con sed pueden realizar una gran labor sobre información almacenada en formato ASCII.
Con ellos podemos realizar labores iguales a la suma de un pequeño gestor de base de datos más una hoja de cálculo.
Supongamos una factura en la que se indica en un fichero los artículos comprados y su precio de venta al público.
Por ejemplo este archivo "compras":
naranjas 5 250
peras 3 120
manzanas 2 360
|
Se trata de un fichero con 3 campos separados por tabuladores. Ahora se desea crear un cuarto campo con el precio total
por cada producto.
$ awk '{total=$2*$3; print $0 , total }' compras
naranjas 6 250 1250
peras 3 120 360
manzanas 2 360 720
|
total es una variable al que se le asignan los valores multiplicados de los campos segundo y tercero,
luego a cada linea se imprime la linea completa ($0) y el total por linea.
Veremos awk en la sección 4.14
3.14.4 cut y paste
En Unix la información solía guardarse en ficheros de texto ASCII organizados por campos verticales separados con un
caracter diferenciador, este solia ser un tabulador o el carácter ":". Una de las necesidades que se suele producir en
estos casos es separar los campos de un fichero y unirlos en otro. cut y paste realizan ese trabajo.
Vamos a usar como ejemplo el fichero /etc/passwd encargado de gestionar los usuarios. Su contenido se compone de
7 campos separados por ":" . Los campos son respectivamente y en este orden login,clave encriptada, identificación de usuario,
identificación de grupo, información del usuario, directorio de inicio de usuario y shell que usa.
He aquí un extracto típico de ese archivo.
decker:x:500:500:Óscar Carrascosa Blanco:/home/murie:/bin/bash
root:x:0:0:root:/root:/bin/bash
practica:x:501:501:Usuario de practicas para Ksh:/home/practica:/bin/ksh
wizardi:x:502:502:Wizard para nethack:/home/wizard:/bin/bash
|
Si por ejemplo ahora queremos ahora emparejar los usuarios con la shell que usa debemos cortar los campos 1 y 7.
Manos a la obra:
$ cut -f1,7 -d: /etc/passwd
decker:/bin/bash
root:/bin/bash
practica:/bin/ksh
wizard:/bin/bash
|
-f expecifica los campos a visualizar -d indica el separador a usar ( por defecto el tabulador ) y por último se
encuentra el fichero a listar.
También es posible especificar intervalos de campos:
$ cut -f5-7 -d: /etc/passwd
Óscar Carrascosa Blanco:/home/decker:/bin/bash
root:/root:/bin/bash
Usuario de practicas para Ksh:/home/practica:/bin/ksh
Wizard para nethack:/home/wizard:/bin/bash
|
Ahora supongamos que hemos redirigido la salida con la ayuda de ">" a 2 ficheros y queremos unir las 2 salidas.
Para ello esta paste.
$ paste salida1 salida2
decker:/bin/bash:Óscar Carrascosa Blanco:/home/decker:/bin/bash
root:/bin/bash:root:/root:/bin/bash
practica:/bin/ksh:Usuario de practicas para Ksk:/home/practica:/bin/ksh
wizard:/bin/bash:Wizard para nethack:/home/wizard:/bin/bash
|
3.14.5 Sort
Supongamos ahora que deseamos de un /etc/passwd ordenada según la información de los usuarios.
Para ello recurrimos a sort, la herramienta de ordenación.
$ sort -t: +4 /etc/passwd
murie:x:500:500:Manuel Muriel Cordero:/home/murie:/bin/bash
practica:x:501:501:Usuario de practicas para Ksh:/home/practica:/bin/ksh
wizard:x:502:502:Wizard para nethack:/home/wizard:/bin/bash
root:x:0:0:root:/root:/bin/bash
|
Se puede apreciar que ha ordenado la salida, pero siguiendo el criterio de la tabla ASCII, si deseamos que no diferencia
entre mayúsculas y minúsculas se haría con:
$ sort -t: +4f /etc/passwd
murie:x:500:500:Manuel Muriel Cordero:/home/murie:/bin/bash
root:x:0:0:root:/root:/bin/bash
practica:x:501:501:Usuario de practicas para Ksh:/home/practica:/bin/ksh
wizard:x:502:502:Wizard para nethack:/home/wizard:/bin/bash
|
-t es la opción que indica que separador se usa.
+4 se refiere a cuantos campos hay que saltar antes de iniciar la ordenación.
f indica que se ignore las diferencias entre mayúsculas y minúsculas
Se puede realizar una ordenación mucho más refinada. Por ejemplo ordenado primero de forma inversa según el shell que
use y luego por la iformación del usuario:
$ sort -t: +6r +4f /etc/passwd
practica:x:501:501:Usuario de practicas para Ksh:/home/practica:/bin/ksh
murie:x:500:500:Manuel Muriel Cordero:/home/murie:/bin/bash
root:x:0:0:root:/root:/bin/bash
wizard:x:502:502:Wizard para nethack:/home/wizard:/bin/bash
|
Ahora supóngase que disponemos un fichero en el que se archiva las personas y las deudas en cuestión de préstamos
que tiene con usted.
Un ejemplo "manga":
$ sort -t: +6r +4f /etc/passwd
Son Goku:23450
Son Gohan:4570
Picolo:356700
Ranma 1/2:700
|
Si usted desea saber a cual primero le tiene que mandar el matón :-) deseará obtener una salida ordenada según morosos.
Si se realiza esta prueba se puede observar que:
$ sort +1 deudas
Ranma 1/2:700
Son Gohan:4570
Son Goku:23450
Picolo:356700
|
Esto no es precisamente lo que se espera debido a que se ha realizado una busqueda alfabética con un número diferente
de digitos. La solución reside en la opcion n:
$ sort +1n deudas
Picolo:356700
Son Goku:23450
Son Gohan:4570
Ranma 1/2:700
|
Las opciones básicas de sort son estas:
+n.mse salta los n primeros campos y los m siguientes caracteres antes de empezar la ordenación.
-n.m para la ordenación al llegar al n-esimo campo y m caracteres despues
y como modificadores de los parámetros son:
-b ignora espacios en blanco iniciales
-d orden de diccionario ( solo se tienen en cuenta letras , numeros y espacios)
-f ignora la distinción entre mayúsculas y minúsculas.
-n orden aritmético
-r orden inverso
3.14.6 wc
wc como se ha visto ya es un contador de letras,lineas y palabras. Por defecto al introducir como parametro un
fichero da como salida el número de lineas, palabras y caracteres que lo componen
Con las opciones podemos variar esa salida:
-l solo da las lineas
-w solo da las palabras
-c solo da los caracteres
3.14.7 Herramientas de diferenciación: cmp, comm y diff
A veces es necesario saber en que se diferencián 2 versiones de un fichero. Este se usa especialmente en programación
cuando hay varias personas trabajando en un mismo proyecto modificando los fuentes de los programas. Si se precisa
saber las variaciones de una versión de otra se recurre a estas herramientas.
cmp es la más básica de todas. Compara dos ficheros e indica, si la hay, el lugar donde se produce la primera
diferencia (número de caracter y línea de la diferencia)
$ cmp antigua nueva
antigua nueva differ: char 11234, line 333
|
comm es algo más avanzado. Su salida se produce en 3 columnas. La primera contiene las lineas únicas del primer
fichero, la segunda las únicas del segunda y la tercera las comunes. Dispone de parametros númericos que establecen
si deseas eliminar alguna de esas columnas. Son la -1 , -2 y -3 que indican a comm que no visualizen la primera,
segunda y tercera columna. Con este ejemplo se observa las lineas que sólo están en el primer fichero y las comunes.
Por último está diff. Es una herramienta fundamental en programación de proyectos avanzados. Si alguna vez ha
bajado un kernel para compilarlo sabrá que puede bajarse los fuentes de la nueva o bajarse el patch con respecto a la
anterior, que suele ser un fichero más pequeño . Ese patch suele terminar en .diff lo que indica que es el resultado
de una salida diff. Esta herramienta contiene una serie de comandos de editor ( vi , rcs ) de tal manera que se hagan
idénticos los 2 ficheros. También es aplicable a directorios y todos los archivos que lo contienen. La utilidad es muy
clara, al tener que bajar menor cantidad de fuente ( solo los cambios ) se aplica el parche ( con patch ) y se vuelve
a compilar. Sin parametros la salida específica en estos formatos como se deben hacer los cambios de tal manera que el
primero sea igual al segundo con comandos vi.
$ diff antigua nueva
3c3
< El Hobbit
---
> El Señor de los Anillos
78a79,87
> Tres anillos para los Reyes Elfos bajo el cielo.
> Siete pera los Señores Enanos en casas de piedra.
> Nueve para los Hombres Mortales condenados a morir.
> Uno para el Señor Oscuro, sobre el trono oscuro
> en la tierra de Mordor donde se extienden las Sombras.
> Un Anillo para gobernarlos a todos. Un Anillo para encontrarlos,
> un anillo para atraerlos a todos y atarlos a las tinieblas
> en la tierra de Mordor donde se extienden las Sombras.
|
3c3 expresa que se debe cambiar la línea 3 , quitando "El Hobbit" y substituyendolo por "El Señor de los Anillos".
78a79,87 indica que se debe insertar unas nuevas lineas 79 a la 87.
3.14.8 uniq
uniq es el encargado de eliminar las redundancias. Por ejemplo si deseamos obtener un listado de la gente
conectada al ordenador en un momento dado deberemos hacer uso de who y de cut.
$ who | cut -f1 -d' '
decker
decker
root
tyfany
|
El resultado no es del todo perfecto. Falta eliminar la doble aparición de decker. Dicho y hecho.
$ who | cut -f1 -d' ' | uniq
decker
root
tyfany
|
Como nota indicar que el -d' ' indica que el separador es el espacio en blanco ya que who no separa usando
tabuladores.