Filtros de Texto UNIX ... y aprenda a manejar vi de una vez. César A. Cabrera E. PULPA: Grupo de Usuarios Linux Universidad Tecnológica de Pereira ¿Qué son los filtros? Algún texto de entrada Filtro (Procesa texto) Ejemplos: tail, head, wc, sort, tr. Grep, egrep, vgrep, fgrep. Awk, perl, sed. Algun texto de salida Fuentes de texto Se sugiere trabajar con who, ps, ls, find y usar redirección de flujos y/o entubamientos (pipes). Casi todos permiten especificar un archivo origen (passwd, shadow, archivos en /etc, etc). NOTA: He aquí la versatilidad del modelo de los flujos, yo no tengo que saber de dónde provienen mis datos, sólo los uso. Cualquier cosa que me arroje datos es un flujo de entrada (puede ser el hardware mismo). Los más simples: Filtricos sort (Ordenar): Muestra la entrada ordenada por algún campo. -f :ignorar caso, -n :orden numérico, -nr :numérico descendente, +# :ignorar primeros # campos (separados por $FS), -o <archivo de salida>, -u :líneas únicas solamente. wc (Word Count): Cuenta líneas, palabras y caracteres de la entrada. -l :Líneas, -c :Caracteres tr (Transliterar): Reemplaza en la entrada los caracteres indicados. tr <patrón a cambiar> <patron de reemplazo> tail (Cola) , head (Cabeza): Muestra solamente algunas líneas de la cola o la cabeza de la entrada (o archivo). tail -# :mostrar últimas # líneas, +# : mostrar a partir de # líneas NOTA: Otros filtros curiositos son uniq [-c -d -u], comm, cmp y diff Ejemplo de filtricos >$who | sort >$who | wc -l >$ls -l | sort +3nr Como guión de shell: cat $* | tr -sc A-Za-z '\012' | sort | uniq -c | sort -n| tail Meter como guión de shell el anterior contenido lee de su entrada 10 nombres de archivos (máximo) e imprime las 10 más comunes. tr -s : sustituír repetidos, -c : complementar set de caracteres. Nuestro viejo amigo 'ed' Editor de texto orientado a la línea y fué el primer editor de texto que hubo en Unix. Combinado con el shell es el origen de las expresiones regulares. Órdenes para ejecutarlas sobre el contenido. a : adicionar texto (finalizar entrada de texto con una línea de sólo un . de contenido). Formato de ordenes: [indicador de líneas] {a|d|s|p|w|q}: añadir, eliminar, sustituir, imprimir, guardar y salir respectivamente. Indicador de líneas: #ini,#fin lineas comprendidas desde línea #ini hasta línea #fin #ini y #fin pueden ser . (línea actual), $ (línea final) o una expresión matemática con ellas (.+1 : línea siguiente a la actual; $-3 : antepenúltima línea). g/<regular-expresion>/ : Ejecuta la acción sobre las líneas que contengan la expresión regular. Nos veremos en el vi!!! (el infierno para muchos legos). Familia Grep grep proviene del comando g/regular-expresion/ de nuestro amigo ed. Imprime las líneas que contengan la expresión o patrón indicado. Sintaxis grep [-v |-n |-y |-b ] <patrón> <archivos> -v efecto inverso, -n imprimir número de línea, -y acoplar minúsculas con mayúsculas (mayúsculas siguen acoplando sólo con mayúsculas). El resto de la familia fgrep : Busca en paralelo por varias líneas de texto. egrep: Busca usando verdaderas expresiones regulares (con operadores de disjunción, cerradura y agrupamiento). La opción -f <nombre_archivo>: Lee el patrón de un archivo. NOTA: Debido a que algunos caracteres de las expresiones regulares de la familia grep se confunden o tienen otros significados para el shell, los patrones deben ser protegidos con apóstrofes o insertados en un archivo aparte. Expresiones regulares Carácter Significado ^ inicio de línea $ fin de línea (debe ir a final de la expresion) . cualquier carácter (debe ir al principio de la expresión). *, + y ? * = 0 a n ocurrencias del carácter anterior, + = 1 a n ocurrencias y ? = sólo una ocurrencia. xy* reconocería xyyyy pero no xyxyxy. asd | qwe una ocurrencia de asd o de qwe. [asd] una ocurrencia de 'a' o de 's' o de 'd' [^asd] no ocurrencia de 'a' o de 's' o de 'd'. O lo que es lo mismo una ocurrencia de cualquier carácter que no sea 'a' o 's' o 'd'. \c no interpretar c (buscar $: \$). Es decir escapar c. Ejemplos de grep >$ls -l | grep '^d' >$ls -l | grep '^........rw' >$grep '^[^:]*::' /etc/passwd Como un sólo comando >$echo '^[^aeiou]*a[^aeiou]*e[^aeiou]*i[^aeiou]*o[^aeiou]*u[^aeiou]*$' >alphavowels.pat >$fgrep -f alphavowels.pat /usr/dict/web2 Se supone que /usr/dict/web2 es un diccionario de palabras. NOTA: El diccionario de palabras de Linux generalmente es /usr/share/dict/linux.words El hermanito de ed: sed sed proviene de 'serial ed' Sintaxis: sed [-n] <lista de comandos ed> <archivos+> La opción -n deshabilita la impresión de línea por defecto. Sed aplica las órdenes dadas una a una e imprime cada renglón con los cambios efectuados. Ejemplo: >$who|sed 's/ .* / /' ... y también acepta la opcion -f <archivo> No permite expresiones aritmeticas: $-5 es ilegal. Más sed /patrón/ es un indicador de línea que acopla con las líneas que coincidan con el patrón. /patrón/! Es la negación del patrón, es decir, las líneas que no coincidan con el patrón. s/patrón/reemplazo/g busca y reemplaza (sustituye) el patrón en la línea donde se encuentre. Dado que sólo reemplaza la primera ocurrencia, la g indica que reemplace todas las que haya en la línea. ^siempre significa inicio y $ siempre significa fin bien sea fin de línea o ultima línea, según el contexto. p. ej.: sed '^$q' muestra todas las líneas hasta llegar a una línea vacía. r <archivo> inserta un archivo a la salida estandar. = imprimir número de línea. AWK: El lenguaje de manejo y procesamiento de patrones. El nombre AWK proviene de los creadores:Alho, Al; Weinberger, Peter y Kernigham, Brian. Es un filtro que tiene su propio lenguaje y permite la manipulación de texto de la manera más flexible posible, usando funciones, condiciones, ciclos y estructuras especiales para ejecutar al inicio y al final del procesamiento. Sintaxis: >$awk 'programa' <archivos> Donde programa es una serie de líneas con la forma /patrón/ {comandos}, si /patrón/ no existe la acción se ejecuta para cada línea y si la acción no existe sólo se imprime la línea. Acepta la opción -f <archivo> Variables en awk Variable Significado FS Separador de campos NF línea. Número de Campos reconocidos en la NR Número de registros leídos actualmente. FILENAME Adivinen! RS Separador de registros (\n por defecto). OFMT Output: Formato de números (de salida). OFS Output: Separador de campos. ORS Output: Separador de registros. Campos en awk Campos Cada parte de la línea que está separada por FS (que por defecto es tab o espacios). El separador se puede cambiar con la opcion -F<sep> o asignando un valor a FS en el programa. Cada campo se referencia anteponiendo $ a su posición en el renglón: $1 es el primero (contando desde 1). No confundir con variables, las variables no tienen adornos (como sí lo hacen en shell). La cantidad de campos leídos es NF. Ejemplo: cat /etc/passwd | awk -F: '{print $1}' $0 es la línea actual entera. Funciones en awk print : Imprime lo que se desee imprimir para la línea especificada por el patrón: awk '{print NR, $0}' printf : %s”, NR, $0 }' Usa el formato de printf para imprimir: awk '{printf “%4d, getline(): Devuelve 0 si es fin de archivo y 1 si no lo es. index(s1,s2): Posicion de s2 en s1, 0 si no está. length(s): Longitud de la cadena s. split(s, a, c): Divide la cadena s usando c como separador y lo almacena en a. Devuelve la cantidad de partes en que se dividió la cadena. substr(s,m,n): Subcadena de s empezando en m y terminando en m+n. int(expresión): Parte entera de expresión. sin(ex), cos(ex), exp(ex) y log(ex): Seno, Coseno, Exponencial y logaritmo natural respectivamente. BEGIN y END para awk BEGIN es un patrón cuyos comandos se ejecutan antes de leer la primera línea. END es el patrón cuyos comandos se ejecutan después de leer la última línea. Ejemplos: >$awk 'BEGIN {FS=”:”} >$2==”” ' >$awk 'END {print NR}' Ejemplos Programa para contar frecuencia de palabras >$cat <<FIN >contar.awk { for (i=1; i<NF ; i++) num[$i]++ } END { for (word in num) print word, num[word] } FIN >$awk -f contar.txt <archivo de texto> Programa para identificar palabras repetidas >$cat <<FIN >repetidas.awk FILENAME != prevfile { NR=1 prevfile=FILENAME } NF>0 { if ($1==lastword) printf “Duplicada %s en %s, línea %d\n”, $1, FILENAME, NR for ( i=2; i<NF; i++) if( $i==$(i-1)) if(NF>0) printf “Duplicada %s en %s, línea %d, $i\n”, FILENAME, NR lastword=$NF } FIN >$awk -f repetidas.awk <archivo de texto> Más detalles AWK es un programa muy potente y complejo, explorarlo no es de sólo recibir una charlita y ya, hay que explorarlo ensayando y leyendo el manual(es) que haya a la mano. Print y printf aceptan redireccionamientos como si estuvieran en shell (print >, >> y |). Patrón, patrón como selector: si se usa patrón, patrón como selector esto equivale a las líneas donde ocurra el primer patrón hasta la línea donde aparezca el segundo patrón. Ejemplo NR==10, NR==20 equivale a las líneas de 10 a 20. Diseño de programas en Unix: Todos los programas de utilidad de Unix comparten un diseño común, todos leen por defecto de la entrada estandar y escriben a la salida estandar (lo que permite el uso de redireccionamientos e interconexiones) generalmente (y por defecto) sin encabezados ni adornos (para que cada línea sea un objeto de interés). Los parámetros opcionales van siempre antes de los nombres de archivo a los cuales se aplica el comando y finalmente, escriben los errores en el error estandar lo cual hace que estos no se pierdan en las interconexiones ni en los redireccionamientos. Y el vi? Vi es sucesor de ed. Tiene dos formas de trabajar: modo de edición y modo de comandos. Al modo de edición se entra con 'i', 'a'. Al modo de comandos se entra por defecto y con la tecla 'esc'. Para poder escribir hay que estar en modo de edición, para poder ejecutar acciones (buscar, reemplazar, numerar, etc.) hay que estar en modo comandos. Escribir en vi Entrar a modo edición Escribir 'a' en modo comandos: permite adicionar texto después de la letra en la que se encuentra el cursor. 'A' adiciona al final de la línea. Escribir 'i' en modo comandos: permite insertar texto justo donde se encuentra el cursor. 'I' inserta al comienzo de la línea. 'o' agrega una línea debajo de la actual. 'O' la agrega encima de la actual. 'c' cambia el carácter en el que se encuentra el cursor. Otros comandos útiles 'u' deshace el último comando dado. '.' repite la última acción 'd' elimina el carácter en el que se encuentra el cursor. Igual que 'x'. 'dd' elimina la línea entera <número>comando: hace el comando las veces que diga número: 10x elimina 10 caracteres desde donde está el cursor. Movimiento vi Movimiento del cursor Fin de línea $ Inicio de línea 0 Palabra siguiente e|w Palabra anterior b Copiar línea yy Pegar línea p Salir :q Guardar :w <nombre> Salir y guardar :x Lo interesante Buscar palabras: escribir '/patrón/' en modo comandos. Ensayar con los patrones. Reemplazar en la línea actual: escribir ':s /pat_busqueda/reemplazo/' Se le pueden poner modificadores después del último '/' (Probar con g, y, m). Qué pasa con el selector de líneas: puede ser . (línea actual), $ (última línea) o un número. P.ej.: 1,. d elimina las líneas desde la primera hasta la actual! Más vi :set number : Numera las líneas :set nonumber : Quita la numeración de líneas :r <archivo> : Inserta el archivo donde esté el cursor. :<número> : Pone el cursor en la línea que diga <número> n : Muestra la siguiente ocurrencia de la última búsqueda realizada. N busca la anterior. Ctrl-g: Muestra el nombre del archivo, línea, columna, etc. en el que se está trabajando. Referencias Esta presentación se encuentra disponible públicamente en el sitio del grupo de usuarios Linux de Pereira (Universidad Tecnológica de Pereira): ??? El tema ha sido seleccionado del libro “El entorno de programación Unix”, Brian Kernighan, Rob Pike, editorial Prentice Hall 1987. Alguna ayuda fué tomada del e-book Mandrake 8.2 Reerence Guide. RTFM : Read The Fucking Manual.