Gentoo Logo

Renuncia de responsabilidad: La versión original de este artículo fue publicada por IBM developerWorks y es propiedad de Westtech Information Services. Este documento es una versión actualizada del artículo original y contiene mejoras introducidas por el Equipo de Documentación de Gentoo.
Este documento carece de soporte activo.


Awk mediante ejemplos, Parte 1

Contenido:

1.  Introducción al gran lenguaje de nombre extraño

En defensa de awk

En esta serie de artículos, te transformaré en un programador productivo con awk. Admito que awk no tiene un nombre particularmente bonito, y que la versión GNU de awk, llamada gawk, suena simplemente rara. Los que no estén familiarizados con el lenguaje, al oír "awk" podrían pensar en un código desordenado y anticuado que sería capaz de volver loco al más avispado de los gurús de UNIX (haciéndole gritar repetidamente "kill -s9!" mientras corre hacia la máquina de café).

Awk no tiene un gran nombre. Pero es un gran lenguaje. Awk está enfocado al procesamiento de texto y la generación de reportes, pero también posee muchas características que permiten la programación seria. Y, al contrario que otros lenguajes, la sintaxis de awk es bastante familiar y toma prestados algunos de los mejores elementos de lenguajes como C, python y bash (aunque técnicamente, awk fue creado antes que python y bash. Awk es uno de estos lenguajes que, una vez aprendido se convierte en una parte imprescindible de nuestro arsenal de codificación.

El primer awk

Listado de Código 1.1: El primer awk

$ awk '{ print }' /etc/passwd

Deberías ver el contenido de tu archivo /etc/passwd aparecer ante tus ojos. Ahora explicaré lo que awk ha hecho. Cuando lo hemos llamado, hemos especificado /etc/passwd como nuestro fichero de entrada. Al ejecutar awk, éste ha evaluado el comando para cada línea de /etc/passwd, por orden. Toda la salida se manda a stdout, y así obtenemos una salida idéntica a la que nos daría el comando cat sobre el fichero /etc/pass.

Y ahora, una explicación sobre el bloque de código { print }. En awk, las llaves se usan para englobar bloques de código, de forma similar a C. Dentro de nuestro bloque de código, tenemos un solo comando print. En awk, cuando un comando print aparece solo, se imprime todo el contenido de la línea.

Listado de Código 1.2: Imprimiendo la línea actual

$ awk '{ print $0 }' /etc/passwd
$ awk '{ print "" }' /etc/passwd

En awk, la variable $0 representa la línea actual, por tanto, print y print $0 hacen exactamente lo mismo.

Listado de Código 1.3: Llenar la pantalla con un texto

$ awk '{ print "hiya" }' /etc/passwd

Múltiples campos

Listado de Código 1.4: print $1

$ awk -F":" '{ print $1 $3 }' /etc/passwd
halt7
operator11
root0
shutdown6
sync5
bin1
....etc.

Listado de Código 1.5: print $1 $3

$ awk -F":" '{ print $1 " " $3 }' /etc/passwd

Listado de Código 1.6: $1$3

$ awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' /etc/passwd
username: halt          uid:7
username: operator      uid:11
username: root          uid:0
username: shutdown      uid:6
username: sync          uid:5
username: bin           uid:1
... etc.

Guiones externos

Listado de Código 1.7: Guión de ejemplo

BEGIN { FS=":" }
{ print $1 }

La diferencia entre estos dos métodos consiste en cómo establecemos el separador de campo. En este guión, el separador es especificado en el mismo código (estableciendo la variable FS). Mientras que en nuestro anterior ejemplo, FS se establece a través de la opción -F":". Por norma general es mejor establecerlo dentro del mismo guión, simplemente porque será un argumento menos que tendrás que tendrás que recordar teclear. Cubriremos la variable FS en más detalle más tarde en este mismo artículo.

The BEGIN and END blocks

Normalmente, awk ejecuta cada bloque de código del guión una vez por cada línea de entrada que se le proporcione. Sin embargo, hay muchas situaciones donde se necesita ejecutar un código de inicialización antes de que awk comience a procesar el texto del fichero de entrada. Para dichas situaciones, awk permite definir un bloque BEGIN. Hemos usado un bloque BEGIN en el ejemplo anterior. Como el bloque BEGIN es evaluado antes de que awk procese el fichero de entrada, es un lugar excelente para inicializar la variable FS (separador de campo), imprimir una cabecera o inicializar otras variables globales a las que podrás hacer referencia más adelante en el programa.

Awk también provee otro bloque especial, llamado el bloque END. Awk ejecuta este bloque tras procesar todas las líneas del fichero de entrada. Típicamente, el bloque END se usa para realizar cálculos finales o imprimir sumarios que deban aparecer al final del flujo de salida.

Expresiones regulares y bloques

Listado de Código 1.8: Regular expressions and blocks

/foo/ { print }
/[0-9]+\.[0-9]*/ { print }

Expresiones y bloques

Listado de Código 1.9: fredprint

$1 == "fred" { print $3 }

Listado de Código 1.10: root

$5 ~ /root/ { print $3 }

Sentencias condicionales

Listado de Código 1.11: if

{
    if ( $5 ~ /root/ ) {
        print $3
    }
}

Ambos guiones funcionan de forma idéntica. En el primer ejemplo, la expresión booleana se pone fuera del bloque, mientras que en el segundo, el bloque se ejecuta para cada línea, y la impresión se realiza de forma selectiva usando una sentencia if. Ambos métodos son válidos, y puedes escoger el que mejor se adapte a tu guión.

Listado de Código 1.12: if if

{
    if ( $1 == "foo" ) {
        if ( $2 == "foo" ) {
            print "uno"
        } else {
            print "one"
        }
    } else if ($1 == "bar" ) {
        print "two"
    } else {
        print "three"
    }
}

Listado de Código 1.13: if

! /matchme/ { print $1 $3 $4 }

Listado de Código 1.14: if

{
    if ( $0 !~ /matchme/ ) {
        print $1 $3 $4
    }
}

Ambos guiones imprimirán solo aquellas líneas que no contengan una cadena de caracteres matchme. De nuevo, puedes escoger el que mejor se ajuste a tu código. Ambos hacen lo mismo.

Listado de Código 1.15: Imprimiendo los campos iguales a foo y bar

( $1 == "foo" ) && ( $2 == "bar" ) { print }

Este ejemplo imprimirá solo aquellas líneas en las que el campo 1 sea igual a foo y el campo 2 sea igual a bar.

¡Variables numéricas!

En el bloque BEGIN, inicializamos nuestra variable entera x a cero. Luego, cada vaz que awk encuentra una línea en blanco, ejecutará la sentencia x=x+1, incrementando x. Tras ser procesadas todas las líneas, el bloque END se ejecutará, y awk imprimirá un sumario final especificando el número total de líneas en blanco que ha encontrado.

Variables no interpretables como numéricas

Listado de Código 1.16: Campo ejemplo

2.01

Listado de Código 1.17: 1.01x$( )1.01

{ print ($1^2)+1 }

Si experimentas un poco, encontrarás que si una variable no contiene un número válido, awk tratará dicha variable numéricamente como un cero si es evaluada en alguna expresión matemática.

Montones de operadores

Otra cosa buena de awk es su gran cantidad complementaria de operadores matemáticos. Además de la suma, la resta, la multiplicación y la división, awk nos permite usar el operador de exponenciación "^", el operador módulo (restos) "%" y y un surtido de operadores de asignación que ha tomado prestados de C.

Estos incluyen pre- y post-incremento/decremento ( i++, --foo ) y operadores de asignación combinados con suma, resta, multiplicación o división ( a+=3, b*=2, c/=2.2, d-=6.2 ). Pero eso no es todo -- también podemos usar útiles operadores de asignación combinados con operaciones de modulo o exponenciación ( a^=2, b%=4 ).

Separadores de campo

Awk tienes su propio surtido de variables especiales. Algunas nos permiten afinar el comportamiento de awk, mientras otras pueden ser leídas para obtener información valiosa sobre la entrada. Ya hemos usado una de estas variables especiales: FS. Como se mención antes, ésta nos permite especificar la secuencia de caracteres que awk espera encontrar como separador de campo. Cuando usamos /etc/passwd como entrada, FS se fijó a ":". Si bien esto funcionó en ese momento, FS nos permite mucha más flexibilidad.

Listado de Código 1.18: Otro separador de campo

FS="\t+"

Arriba usando el operador especial de expresiones regulares "+", que significa "una ocurrencia o más del carácter anterior".

Listado de Código 1.19: Fijando FS como espacio en blanco

FS="[[:space:]+]"

Si bien esta asignación funcionará, no es necesaria. ¿Por qué? Porque por defecto, FS está prefijado como un solo espacio en blanco, que awk interpreta como "uno o más espacios o tabuladores". En este ejemplo en particular, ¡el valor predeterminado de FS era exactamente lo que andabas buscando desde primera hora!

Listado de Código 1.20: Ejemplo de separador de campo

FS="foo[0-9][0-9][0-9]"

Número de campos

Listado de Código 1.21: Número de campos

{
    if ( NF > 2 ) {
        print $1 " " $2 ":" $3
    }
}

Número de registro

Listado de Código 1.22: Número de registro

{
    #saltar cabecera
    if ( NR > 10 ) {
        print "ok, ¡ahora a por la información!"
    }
}

Awk provee variables adicionales que pueden ser usadas para varios propósitos. Veremos más de estas variables en artículos posteriores.

Hemos llegado al final de nuestra exploración inicial de awk. Conforme la serie avance, demostraré funcionalidades más avanzadas de awk, y terminaremos la serie con una aplicación real en awk. Mientras tanto, si quieres aprender más, examina la lista de recursos listada más abajo.

2.  Recursos

Enlaces útiles



Imprimir

Página actualizada 8 de enero, 2008

Sumario: Awk es un lenguaje muy elegante con un nombre un tanto extraño. En este primer artículo de una seria de tres, Daniel Robbins pondrá en forma tus habilidades de programación con awk. Conforme avance la serie, serán tratados temas un poco más avanzados, culminando en una demostración de aplicación real avanzada escrita usando awk.

Daniel Robbins
Autor

Jesús Guerrero
Traductor

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.