Awk

awk es un lenguaje de analisis y procesado orientado a patrones de texto. Esta disenado para hacer sencilla las tareas de recopilacion comun de informacion y manipulacion de texto.

En la practica, el uso de awk suele caer en dos amplias categorias. Una es la que podria considerarse generacion de informes: esto implica el procesado de una entrada para extraer conteos, sumas, subtotales, etcetera. Esto tambien incluye la escritura de programas de validacion de datos triviales, como la verificacion de campos que contengan contiene unicamente informacion numerica o analizar si ciertos delimitadores estan adecuadamente balanceados.

Una segunda categoria es su uso como transformador de datos, convirtiendo datos de la forma producida por un programa en la esperada por otro distinto. Los ejemplos mas simples consisten en simplemente seleccionar campos, y tal vez reorganizarlos.

awk resulta invaluable para el el procesado textual y numerico combinado. Fue creado por Aho, Weinberger y Kerningham en los Laboratorios Bell, de alli su nombre.

Uso basico

La operacion basica de awk es analizar un conjunto de lineas de entrada en orden buscando por coincidencias que se han especificado. Para cada patron, puede especificarse una accion; esta accion sera llevada a cabo en cada linea que muestre coincidencias, y se le dara salida (mostrandolo en la terminal o redirigiendola).

El comando

awk programa [ficheros]

ejecuta los comandos de awk de la cadena programa sobre el conjunto de ficheros especificados, o bien utilizara la entrada estandar si no se hubiesen espeficado fichero alguno para analizar. La declaracion tambien puede volcarse a un fichero_salida con el prefijo -f:

awk -f fichero_salida [ficheros]

Estructura de Programa de awk

Un programa de awk es una secuencia de declaraciones que guardan la siguiente sintaxis:

patron { accion }

A su turno, cada linea de entrada resulta analizada por awk contra cada patron. En cada uno de los patrones en los que encuentre coincidencia, awk ejecutara la accion solicitada. Una vez que awk ha finalizado de evaluar todos los patrones solicitados en la linea, cargara la siguiente linea, y comenzara nuevamente el analisis de la misma.

Como en las declaraciones del programa awk tanto los patrones como acciones son opcionales, como forma de distinguir unos de otras se encierra la accion entre llaves {} para distinguirla.

En cada declaracion del programa pueden omitirse tanto el patron como accion, pero no pueden omitirse ambas:

  • Si la accion carece de patron, la accion se ejecutara para todas las lineas de entrada.
  • Si un patron carece de accion, la linea coincidente se devolvera como salida (una linea con muchos patrones coincidentes sera impresa varias veces).

Naturalmente, las lineas cuyos patrones no resultan en coincidencia, son ignoradas ("filtradas").

Pueden disponerse comentarios en los programas de awk (los cuales tambien resultan filtrados). Estos comienzan con el caracter # y finalizan con el final de la linea, como en

patron { accion } # Este es el comentario de la linea.

Registros y Campos

La entrada de awk se divide en registros finalizados por un separador de registro. El separador de registro por defecto es un caracter ASCII nulo de nueva linea. En cristiano, esto significa que de modo que por defecto, awk procesa la entrada de a una linea por vez. El numero del registro actual se dispone en una variable denominada NR.

Cada registro se considera dividido en campos. Los campos normalmente estan separados por un caracter de espacio (en blanco o de tabulacion). Pero el separador de campo de entrada es asingable. Los campos se refieren como $1, $2, etcetera, siendo $1 el primer campo, $2 el segundo, etc, y donde $0 representa todo el registro de entrada.

Los campos pueden ser asignados. El numero de campos en el registro actual forma parte de la variable denominada NF.

Las variables FS y RS refieren al separador de campo y separador de registro. Estos pueden ser asignadas en cualquier momento por un unico caracter. Por ejemplo, para asignar el caracter c como separador de campo en la variable FS, se utiliza el argumento opcional de linea de comandos -Fc.

En caso que el separador de registro este vacio, se considera como separador de registro por defecto a una linea vacia, y los separadores de campo por defecto son los caracteres en blanco (tabulacion y nuevas linea) se consideran como.

La variable FILENAME se utiliza para contener el nombre del fichero de entrada actual.

Patrones

Un patron en frente de una accion opera como un semaforo que determina si la accion debe ejecutarse.

Como patron podran utilizarse una variedad de expresiones de control: expresiones regulares, expresiones relacionales aritmeticas, expresiones de cadena, y combinaciones booleanas arbitrarias.

Print

La accion mas simple consiste imprimir algo o todo de de un registro; esto se realiza con el comando print de awk.

El siguiente comando imprime todas los registros, copiando la entrada a la salida de forma intacta.

{ print }

Resulta mas util imprimir un campo o campos de cada registro, por ejemplo, imprimir los primeros dos campos en orden inverso:

print $2, $1

Los items separados por , en la declaracion de impresion resultaran separados por el separador de campo de salida actual, cuando se les de salida. Los items que no esten separados por comas en la entrada, resultaran concatenados. De este modo, para correr el primer y segundo campos juntos, se utiliza:

print $1 $2

Se pueden utilizar las variantes predefinidas NF y NR, por ejemplo, para imprimir cada registro precedida por el numero de registro y el numero de campo.

{ print NR, NF, $0 }

La salida puede dividiise en multiples ficheros, por ejemplo el programa siguiente escribe el primer campo $1 en fichero1 y el segundo campo en el fichero2.

{ print $1 >"fichero1"; print $2 >"fichero2" }

Tambien puede usarse la notacion >> para agregar la salida al fichero1 (se lo creara de ser necesario).

print $1 >>"fichero1"

El nombre de fichero puede ser una variable o un campo, asi como una constante. Por ejemplo, para usar los contenidos del campo 2 en el nombre:

print $1 >$2

De forma similar, la salida puede entubarse a otro proceso, por ejemplo:

print | "mail bwk"

envia por correo electronico la salida a bwk.

Las variables OFS y ORS pueden utilizarse para cambiar el separador de campo de salida y el separador de registro de salida. El separador de registro de salida se agrega a la salida de la declaracion print.

Awk tambien provee a la declaracion printf para formateado de salida, con esta sintaxis:

printf expresion, expresion, ....

Por ejemplo, para imprimir $1 como una cifra de coma flotante de 8 cifras, con dos luego de la coma, y $2 como una cifra decimal de 10 digitos, seguida por una nueva linea, se usaria:

printf "%8.2f %10ld\nm $1, $2

Un ejemplo de un programa awk que imprime la tercera y segunda columna de una tabla en dicho orden:

[print $3; $2]

Este imprime todas las lineas de entradas con una A, B, o C en el segundo campo:

$2 ~/A|B|C/]

Este imprime todas las lineas en las cuales el primer campo es diferente que el primer campo previo:

$2 ~/A|B|C/]

BEGIN y END

El patron especial BEGIN coincide con el comienzo de la entrada, antes de que sea leido el primer registro. El patron END coincide con el final de la entrada, despues de que el registro ha sido procesado. Por lo tanto, BEGIN y END proveen una forma de inicializacion o obtencion de control previo, y luego de procesar, como indicativo de cedido de control final.

Por ejemplo, el separador de campo puede configurarse en un ; con:

BEGIN { FS = ";" }
...resto del programa...
...O las lineas de entrada pueden contarse con
END { print NR }

Naturalmente, si BEGIN esta presente en el primer patron, END debe aparecer en el ultimo utilizado.

Expresiones regulares de awk

La expresion regular mas simple es la cadena literal de caracteres, cerrada entre barras /, como /perez/

Esto representa realmente un programa completo de awk que imprimira todas las lineas que contienen cualquier ocurrencia del nombre perez. Si una linea contiene perez como parte de una palabra mas grande, tambien resultara coincidente, como perezoso.

Las expresiones regulares de awk incluyen las formas de expresiones regulares encontradas en el editor ed y grep. Adicionalmente, awk permite agrupar con (), alternar con |, "una o mas" con +, y ? para "cero o uno", todas como en lex. Las clases de caracteres pueden abreviarse: [a–zA–Z0–9] es el conjunto que solicita todas las letras y digitos..

Por ejemplo, este programa de awk:

/[Aa]bad |[Ww]enceslao |[Kk]arina/

...imprimira todas las lineas que contengan cualquiera de los nombres ‘‘Abad,’’ ‘‘Wenceslao’’ o ‘‘Karina,’’ esten en mayusculas o no.

Las expresiones regulares de awk deben encerrarse en barras //, al igual que en ed y sed. Los espacios en blanco u los metacaracteres de expresiones regulares dentro de las expresiones regulares son importantes. Para impedir el interpretado de estos metacaracteres, precedalos con una barra invertida \.

Por ejemplo, este patron busca coincidencias de cualquier cadena de caracteres entrecerrados en las barras: / \/ .∗\//

Tambien podra especificar que cualquier campo o variable busque coincidencia de una expresion regular (o no coincida con ella, utilizano los operadores y !∼.

Por ejemplo, este programa de awk imprime todas las lineas donde el primer campo coincide con juan o Juan. Note que esto tambien coincidira con Juana, San Juan, etcetera.

$1 ∼ /[jJ]uan/

Para restringir la coincidencia a exactamente [jJ]uan, debe usarse:

$1 ∼ /ˆ[jJ]uan$/

El circunflejo ˆ refiere al comienzo de la linea o campo, mientras que el signo pesos $ refderira al final de linea o campo.

Expresiones Relacionales de awk

Los patrones de awk pueden ser expresiones relacionales que incluyan los operadores relacionales convencionales <, <=, ==, !=, >=, y >. Un ejemplo es $2 > $1 + 100 que seleccionara las lineas donde el segundo campo es al menos un 100 mayor que el primer campo. De forma similar,

NF % 2 == 0

...imprime las lineas con el un numero de campos par.

Sin embargo, si ninguno de los operandos sometidos a evaluacion relacionar fuese numerico, se realizara una comparacion de cadenas de caracteres alfabeticos.

Por lo tanto este programa selecciona las lineas que comienzan con una s, t, u, etc.

$1 >= "s"

En la ausencia de cualquier otra informacion que permita dilucidar el contexto, se asumen los campos como cadenas alfabeticas. De esta forma, este programa realizara una comparacion de cadena alfabetica:

$1 > $2

Patrones Combinacionales

Un patron puede ser cualquier operador combinacinoal de logica booleana OR ||, AND &&, y NOT !.

Por ejemplo, este programa de awk selecciona las lineas donde un primer campo comienza con p, pero no sea perez. El && y el || garantizan que sus operandos sean evaluados de izquierda a derecha; la evaluacion se detendra tan pronto como se verifique verdad o falsedad.

$1 >= "p" && $1 < "q" && $1 != "perez"

Rangos de Patrones

El patron que selecciona una accion puede consistir de dos patrones separados por ,, siguiendo la sintaxis

patron1, patron2 { ... }

En este caso, la accion se realiza para cada linea entre una ocurrencia de patron1 y la siguiente ocurrencia de patron2 (inclusive). Por ejemplo, /empezar/, /detener/ imprime todas las lineas existentes entre las cadenas empezar y denener, mientras que NR == 100, NR == 200 { ... } realiza la accion desde la linea 100 hasta la linea 200 de la entrada.

Acciones

Una accion de awk consiste en una secuencia de declaraciones de accion finalizadas por un caracter nulo de nueva linea o bien un ;.

Dichas declaraciones de accion pueden utilizarse para realizar una variedad de tareas de manipulacion de cadenas o de registro.

Las declaraciones pueden ser una de las siguientes:

  • Declaracion if (expresion) [declaracion else]
  • Declaracion while (expresion)
  • Declaracion for (expreion; expresion; expresion)
  • Declaracion for (var en array)
  • Declaracion do while (expresion)
  • break
  • continue
  • { [declaracion ...] }
  • expression (conmunmente var = expression
  • print [expresion-lista] [>expression]
  • printf format [..., expresion-lista] [>expresion]
  • return [expresion]
  • next (saltea los patroners remanentes de esta linea de entrada)
  • nextfile (saltea lo siguiente en este fichero, abre el siguiente, comienza desde arriba)
  • delete array[expression] (borra un elemento del array)
  • delete array (borra todos los elementos del array)
  • exit [expresion] (sale del procesado y realiza procesado END; el status sera expresion)

Funciones Incorporadas

Awk incorpora la funcion lenght para computar la longitud de una cadena de caracteres. Este programa imprimira cada registro, precedido por su longitud:

{print length, $0}

Por si misma, length es una pseudo-variable que abarca la longitud del registro actual; length(argumento) es una funcion que almacena la longitud de su argumento. En este rpgorama, el argumento puede ser cualquier expresion:

{print length($0), $0}

Awk tambien incorpora las funciones aritmeticas raiz cuadrada sqrt, logaritmo log, exponencial exp, e integral int, como parte de sus respectivos argumentos.

El nombre de dichas funciones incorporadas, sin argumento o entre parentesis, significaran un valor de funcion para el registro entero.

Este programa imprime lineas cuya longitud sea inferior a 10 o mayor que 20:

length < 10 || length > 20

La funcion incorporada substr(c, m, n) produce la subcadena c que comienza en una posicion m (origen 1) y tiene como maximo n caracteres de longitud. Si se omite n, la subcadena va hasta el final de la subcadena c.

La funcion incorporada index(c1, c2) devuelve el indice binario donde la cadena c2 ocurre en c1, resultando 0 si no lo hace.

La funcion sprintf(f, e1, e2, ...) produce el valor de las expresiones e1, e2, etc., en el forma printf fespecificado por f.

Por ello, en el ejemplo,

x = sprintf("%8.2f %10ld", $1, $2)

...pondra la x a una cadena producida por el formateo de los valores de $1 y $2.

Variables, Expresiones, y Asignaciones

Las variables de awk toman valores numericos (coma flotante) o cadenas alfabeticas de acuerdo al contexto. Por ejemplo, en x = 1, x es claramente una cifra, mientras que en x = "perez" es claramente una cadena alfanumerica. Las cadenas se convierten a numeros y viceversa, toda vez que el contexto lo demande. Por ejemplo:

x = "3" + "4"

...asigna 7 a la x.

Las cadenas que no pueden ser interpretadas numericamente en un contexto numerico generalmente tienen un valor numerico de 0, pero no suele ser adecuado contar siguiendo este comportamiento.

Por defecto las demas variables que no son las incorporadas, deben inicializarse a una cadena nula, que tiene un valor numerico de 0; esto elimina la necesidad de la mayoria de las secciones de tipo BEGIN. Por ejemplo, la sima de los primeros dos campos puede computarse con:

{ s1 += $1; s2 += $2 }
END { print s1, s2 }

La aritmetica se hace internamente en coma flotante. Los operadores arimeticos son +, , , /, y % (porcentaje o modulo).

Tambien estan disponibles los operadores de C para incremento ++ y decremento ––, asi como los operadores de asignacion +=, –=, ∗=, /=, y %=. Estos operadores pueden utilizarse en las expresiones.

Variables de Campo

Los campos en awk comparten esencialmente todas las propiedades de las variables: pueden usarse en aritmetica u operaciones de cadenas, y tambien pueden ser asignados. Por lo tanto puede reemplazarse el primer campo con un numero de secuencia:

{ $1 = NR; print }

...o bien acumular dos campos en un tercero, asi:

{ $1 = $2 + $3; print $0 }

...o incluso asignar una cadena a un campo:

{ if ($3 > 1000)
$3 = "demasiado grande"
print
}

...lo cual reemplaza el tercer campo con "demasiado grande" cuando lo es, y en cualquier caso imprime el registro.

Pueden usarse expresiones numericas para referenciar campos:

{ print $i, $(i+1), $(i+n) }

...donde la consideracion de campo como numerico o cadena depende del contexto; en casos ambiguos como if ($1 == $2) ... los campos son tratados como cadenas.

Cada linea de entrada se divide en campos automaticamente de ser necesario. Tambien es posible dividir cualquier variable o cadena en campos:

n = split(c, arreglo, sep)

...divide la cadena c en un arreglo[1], ..., arreglo[n]. El numero de elementros encontrados resulta devuelto. Si se provee el argumento sep, se utiliza como separador de campo; de otra forma se usa como separador FS.

Concatenacion de cadenas

Las cadenas pueden concatenarse. Por ejemplo:

length($1 $2 $3)

...revuelve la longitud de los primeros tres campos. O en una declaracion impresa:

print $1 " es " $2

...imprime los dos campos separados por " es ". Las variables y expresiones numericas tambien pueden aparecer en las concatenaciones.

Arreglos de awk

Los elementos de los arreglos de awk no necesitan ser declarados; activan su existencia simplemente por ser mencionados. Los subscriptos pueden tener cualquier valor no nulo, incluyendo cadenas no numericas. Como ejemplo de un subscripto convencional, la declaracion

x[NR] = $0

asigna el registro de entrada actual al elemento nro. NR del arreglo x. De hecho, es posible procesar toda la entrada entera en orden aleatorio con el siguiente programa de awk:

{ x[NR] = $0 }
END { ... programa ... }

La primer accion meramente registra cada linea de entrada en el arreglo x.

Los elementos del arreglo pueden ser nombrados por valores no numericos. Supongamos que la entrada contiene campos con valores como manzana, naranja, etc. Este programa incrementara el contador de los elementos del arreglo nombrado, y los imprime al final de la salida:

/manzana/
/naranja/
END
{ x["manzana"]++ }
{ x["naranja"]++ }
{ print x["manzana"], x["naranja"] }

Declaraciones de control de flujo

Como en el lenguaje C, Awk ofrece las declaraciones basicas de control de flujo if-else, while, for, y el agrupado de declaraciones con llaves {}.

Primero se evalua la condicion entre parentesis (); si es verdadera, se ejecuta la declaracion siguiente a if. La seccion else es opcional.

La declaracion while procede como en C. Para imrpirmir todos los campos de entrada uno por linea:

i = 1
while (i <= NF) {
print $i
++i
}

La declaracion for opera como en C:

for (i = 1; i <= NF; i++)
print $i

...hace lo mismo que la declaracion while explicada. Existe una forma alternativa para la declaracion for que se adecua para acceder a los elementos de un arreglo asociativo:

for (i in array)
statement

...donde la declaracion i activa cada elemento del arreglo. Los elementos son accedidos en un orden aparentemente aleatorio. Surgira el caos is se altera el i, o si cualquier elemento nuevo se accede durante el bucle.

La expresion en la seccion de la condicion de un if, while or for puede incluir operadores relacionales como <, <=, >, >=, == ("igual a"), y != ("no igual a"); coincidencias de expresiones regulares con operadores de coincidencia y !∼; los operadores logicos ||, &&, y !; y por supuesto las parentesis () para agruparlos.

La declaracion break provoca la salida inmediata de un while o for encerrado; la declaracion continue provoca el cimienzo de la siguiente iteracion.

La declaracion next provoca que awk saltee inmediatamente al segundo registro y cominece a analizar los patrones desde el inicio. La declaracion exit provoca que el programa se comporte como si hubiese ocurrido la finalizacion de la entrada.