Jesús Alonso Abad Estrella Resa Camarero

Anuncio
Jesús Alonso Abad
Estrella Resa Camarero
Autómatas y lenguajes formales
Curso 2004-2005 Universidad de Burgos
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
1. Introducción
Awk es un lenguaje de búsqueda y procesamiento de patrones. Su nombre se debe a
las iniciales de sus diseñadores: Alfred V. Aho y Brian W. Kernighan, ambos
desarrolladores de los Laboratorios Bell. Inicialmente se desarrolló para escribir
programas muy cortos, pero sus características se han impulsado más allá de la idea
original de los autores.
Awk está especialmente desarrollado para trabajar con archivos estructurados y
patrones de texto. Dispone de características internas para descomponer líneas de
entrada en campos y comparar estos campos con patrones que se especifiquen. Debido a
estas posibilidades, resulta muy apropiado para trabajar con archivos que contengan
información estructurada en campos, tales como inventarios, listas de correo y bases de
datos simples.
A pesar de que algunos programas en awk constan tan sólo de una línea, son el
equivalente de comandos de un sistema Unix, tales como grep, wc, cut, join o paste. A
diferencia de estas herramientas Unix, awk, al ser un lenguaje de programación, tiene
disponibles funciones, estructuras de control y variables que aumentan su potencia y
funcionalidad.
2. Sintaxis
El cometido básico de awk es buscar líneas en fichero (u otras unidades de texto)
que contengan ciertos patrones. Cuando en una línea se encuentra un patrón, awk realiza
las acciones especificadas para dicho patrón sobre esa línea. Awk sigue el
procesamiento de las líneas de entrada de esta forma hasta que llega al final del fichero.
La sintaxis básica de awk es la siguiente:
donde:
archivo_programa: especifica el archivo fuente del programa a aplicar a archivo.
c: especifica el carácter limitador de campos. Por defecto es el espacio en blanco.
programa: conjunto de patrones e instrucciones a ejecutar. Para evitar conflictos con
la shell, van encerrados entre comillas simples (‘).
variable=valor: se utiliza para establecer los valores que tomarán las variables que
utilice el programa.
archivo: archivo que será procesado por awk. Si se especifica “-“, se interpreta
como la entrada estándar.
Awk también permite introducir comentarios dentro del código. Como ya
conocemos, un comentario es un texto que es incluido dentro de un programa para que
sea más entendible por los usuarios del mismo. En el lenguaje awk, los comentarios
empiezan por el símbolo almohadilla “ #”. Ejemplo:
# Este programa encuentra registros que contengan el patrón th. De esta forma es
# como continúas el comentario en una línea adicional.
2
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Ejemplo simple: programa que busca la cadena de caracteres foo en el fichero de
entrada ‘ Lista-BBS’ y si la encuentra la imprime.
awk ‘/foo/ {print $0}’ Lista-BBS
3. Campos y Variables
La entrada de un programa awk se lee en unidades llamadas registros, y éstos son
procesados por las reglas uno a uno. Cada registro leído se divide automáticamente en
campos, para poder aplicar las reglas más fácilmente. El propósito de los campos es
facilitar al usuario la referencia a las subpartes de un registro.
La nomenclatura de los campos está compuesta por el símbolo dólar seguido del
número de campo que se desee ($1, $2,etc). Símbolos de puntuación tales como el
punto pueden ser incluidos dentro de un campo. $0 indica la línea entera. Estas variables
se pueden manipular (sumar, cambiar, etc.) como cualquier otra variable, por ejemplo:
awk '{ $1 = $2 + $3; print $0 }'archivo Este programa suma los campos dos y tres
en el campo uno e imprime el nuevo registro (es decir, la línea completa representada
por $0)
El último campo de un registro puede ser representado por $NF. Si se intenta
acceder a un número de campo mayor que el último, el resultado es la cadena vacía. Del
mismo modo, el número de registros leídos se guarda en la variable NR.
Los campos se pueden referir con expresiones numéricas. Si el resultado de evaluar
la expresión no es un entero, se trunca la parte decimal. Un campo puede ser tratado
como texto o como número, pero todo depende del contexto. Si se producen
ambigüedades, awk tratará los campos como texto. Un ejemplo donde se requiere
analizar el contexto es:
x= “2”+”2” En este caso awk asigna a x el valor 4, como si hubiera escrito 2+2,
ya que el contexto demanda un valor númerico.
Los registros están separados por un carácter llamado separador de registros, que
por defecto es el carácter newline. Por lo tanto, normalmente, un registro se corresponde
con una línea de texto. Se puede usar un carácter diferente mediante la llamada a la
variable RS (record separator), inicializada por defecto a “\n”.
La variable RS puede tener cualquier cadena como valor, aunque sólo el primer
carácter se tomará como el separador de espacios. El valor de RS se puede cambiar a
través del operador de asignación ‘=’. El nuevo carácter separador estará entre comillas
para que sea interpretado una constante cadena. Ejemplo:
awk ‘BEGIN {RS = “ / ”}; {printf $0}’ Lista-BBS
Por ejemplo, para la entrada
Carlos:estudiante
3
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Luis:profesor
Con RS=”\n” generaría los registros
$0= Carlos:estudiante
$0= Luis:profesor
Mientras que RS=”:” generaría
$0= Carlos
$0= estudiante
Luis
$0= profesor
Nota: el momento adecuado para realizar el cambio del separador de campos por
defecto a otro, es el comienzo del programa.
Respecto a las variables, no hace falta indicar el tipo al que pertenecen, ya que esta
operación se realiza automáticamente en función del contexto. Por defecto las variables
se inicializan a la cadena nula o al valor numérico 0, por lo que no es necesario
inicializarlas.
Como ya hemos comentado, existen una serie de variables predefinidas en awk que
pueden ser utilizadas dentro de un programa. Aparte de las ya mencionadas existen las
siguientes:
OFS( output file separator): separador del campo de salida.
ORS ( output register separator): separador del registro de salida.
RSTART: posición de la cadena en la que se verifica el patrón utilizado,
comenzando desde 1.
RLENGTH: longitud de la cadena en la que se verifica el patrón utilizado.
SUBSEP: separador de cadenas en arrays multidimensionales.
4. Formato de los estamentos
Se llaman estamentos a los conjuntos patrón-acción, que componen el núcleo de un
programa awk, ya que especifican las acciones que se llevan a cabo cuando la entrada
concuerda con cierto patrón. La sintaxis de los estamentos es:
Patrón {acción}
Para cada línea que verifique lo especificado en el patrón, se ejecutará la acción
indicada.
La acción siempre va encerrada entre llaves.
Se pueden especificar varias acciones, separándolas por “;” o por el carácter de
nueva línea “\n”.
Si no se especifica la acción, se mostrarán por pantalla aquellas líneas que
contengan el patrón. Dentro de las acciones puede haber sentencias de control,
operaciones matemáticas o de concatenación.
Los patrones deben ir rodeados por caracteres “/” y pueden contener más de un
patrón separados por comas. En esta caso la acción se aplicará a aquellas líneas
comprendidas entre la aparición del primer y último patrón definido. Si se utiliza el
símbolo “ ~” se indica que el patrón de la derecha se incluye en el campo de la
izquierda. Si el símbolo utilizado es el “!~”, el patrón de la derecha no es incluido en
el campo de la izquierda. Ejemplos:
4
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
1. $1 ~ /foo/ es la contraposición al ejemplo de patrón S1==”foo”, que indica que el
primer campo del registro de entrada tiene que coincidir con foo. En nuestro caso,
el patrón indicará que el primer registro de entrada tiene que incluir la palabra foo.
Si ponemos el siguiente ejemplo, $1 ¡~/foo/, indicaremos que el primer campo del
registro de entrada no debe contener la palabra foo.
2. Pongamos ahora un ejemplo más elaborado:
awk ‘{ if ($1 ~ /J/) print }’ inventario Esta expresión regular selecciona todos los
registros de entrada que contengan la letra ‘J’ en cualquier posición dentro del
primer campo.
5. Patrones
Los patrones controlan la ejecución de reglas: una regla es ejecutada cuando su
patrón concuerda con el registro de la entrada actual. A continuación mostramos un
sumario de los patrones soportados en awk:
•
Patrón vacío o null: casa con todos y cada uno de los registros de entrada.
Ejemplo: awk ‘{print $1}’ Lista-BBS imprime el primer campo de cada registro.
•
/ expresiones regulares /: La concordancia se produce cuando el texto del registro
de entrada pertenece al lenguaje especificado por la expresión regular. Son usadas
como patrón encerradas entre las barras “/”. Pueden combinarse con caracteres
llamados operadores de expresiones o metacaracteres para incrementar su
versatilidad. A continuación mostramos una tabla de ejemplo:
Metacarácter
^
$
.
[…]
[^…]
|
Significado
Busca el principio de una
cadena o de una línea
dentro de una cadena
Busca el carácter que
concuerda al final o
principio de una cadena
Concuerda con cualquier
carácter único, excepto el
carácter nueva línea
Ejemplo
@capitulo
p$ ---> concuerda con un
registro que acabe en p
U.A --> concuerda con
cualquier expresión que
comience con U y acabe
en A
Concuerda con cualquiera [0-9] ---> concuerda con
de
los
caracteres cualquier dígito
encerrados entre corchetes
Concuerda
con
el [^0-9] ---> concuerda con
conjunto de caracteres todo lo que no sea un
complementario
dígito
Se usa para especificar ^p|[0-9] ---> concuerda
alternativas
con cualquier cadena que
tenga
un
dígito
o
comience por p
5
Lenguaje awk
()
*
+
?
\
Jesús Alonso Abad
Estrella Resa Camarero
Agrupan
expresiones
regulares
Repetición de la expresión
regular precedente
La expresión regular
precedente debe concordar
al menos una vez
La
expresión
puede
concordar una vez o
ninguna
Se usa pasa suprimir el
significado especial de un
carácter
ph*
wh+y --> concuerda con
why, pero no con wy
fe?d -->concuerda con fed
o fd
Los operadores de mayor preferencia son “*”, ”+” y “?”, seguidos por concatenación
y |. Los paréntesis pueden hacer que el modo de agrupar los operadores cambie.
•
expresiones: se considera que se ha producido una concordancia con una expresión
cuando su valor, convertido a número, es distinto de cero ( si es un número) o no
nulo ( si es una cadena).
Existen un par de casos especiales de expresiones como patrones: los patrones de
comparación y los patrones booleanos.
Patrones de comparación: chequean relaciones como igualdad entre dos cadenas o
números. Se construyen a través de los operadores relacionales, cuyas formas coinciden
con las propias del lenguaje C en los operadores de igualdad, desigualdad y
comparación. Se incluyen dos nuevas formas propias del awk que son las siguientes:
x~y
Verdad si x concuerda con la expresión regular descrita por y
x!~ y
Verdad si x no concuerda con la expresión regular descrita por y
Ejemplos:
awk ‘$1 = = “foo” {print $2}’ Lista-BBS imprime el segundo campo de cada
registro de entrada cuyo primer campo valga justamente ‘foo’.
awk ‘$1 ~ /foo/ {print $2}’ Lista-BBS acepta cualquier registro con un primer
campo que contenga la cadena ‘foo’.
Patrones booleanos: es una expresión que combina otros patrones utilizando los
operadores booleanos”o” (‘||’), ”y” (‘&&’) y “not” (‘!’). Los patrones que relacionan
los patrones booleanos pueden ser expresiones regulares o cualquier otro tipo de
expresión.
Ejemplos:
awk ‘/2400/ && /foo/’ Lista-BBS imprime todos los registros del fichero de
entrada Lista-BBS que contengan tanto ‘2400’ como ‘foo’.
awk ‘! /foo/’ Lista-BBS imprime todos los registros del fichero de entrada ListaBBS que no contengan la cadena ‘foo’.
•
pat1, pat2 : Un par de patrones separados por una coma especifica un rango de
registros que encaja con rangos de registros de entrada consecutivos. Ejemplo:
6
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
awk ‘$1 == “on”, $1 == “off ” ’ imprime todos los registros entre ‘on’/’off’ ambos
incluidos.
•
Patrones especiales BEGIN y END: Son etiquetas especiales que suministran a
awk información para antes del inicio del procesamiento o al final del mismo. En el
caso de BEGIN la acción indicada se ejecutará antes de leer cualquier entrada,
mientras que la acción asociada a END se ejecutará una vez realizado el tratamiento
de los registros de entrada. La etiqueta END suele utilizarse para imprimir
resultados totales o cálculos realizados con los registros leídos. Ejemplo:
awk ‘ BEGIN {print “análisis de foo”}
/foo/ {++foobar}
END { print “foo aparece”, foobar, “veces.”}’ Lista-BBS
Este programa averigua cuántas veces aparece la cadena “foo” en el fichero de
entrada Lista-BBS. BEGIN imprime un título para el informe. La segunda regla
incrementa el valor de la variable foobar cada vez que se lee de la entrada un registro
que contiene el patrón “foo”. Finalmente, END imprime el valor de la variable foobar
al final de la ejecución.
6. Acciones:
una acción consiste en una o más sentencias awk encerradas entre
llaves. Cada sentencia especifica una cosa que se tiene que hacer. Las sentencias son
separadas por el caracter de nueva línea o por el carácter punto y coma si conviven más
de una sentencia en la misma línea. A continuación mostramos los tipos de sentencias
soportadas en awk:
•
Expresiones: pueden llamar a funciones o asignar valores a variables. La ejecución
de este tipo de sentencias simplemente calcula el valor de la expresión.
•
Sentencias de control: especifican el flujo de control del programa awk. Se pueden
utilizar varias sentencias propias de C, tales como if-else, bucles for, while, dowhile y sentencias break y continue. A continuación pondremos un ejemplo con
cada una de ellas:
Sentencia if - else: determinar si un número es par o impar.
if (x %2 ==0)
print “ x es par”
else
print “ x es impar”
Este es el formato de la sentencia if. Ahora ilustraremos cómo quedaría un programa
awk si esta sentencia formara parte de la acción:
awk ‘{ if ( x %2==0) printf “x es par”; else
printf “x es impar”}’
Nota: si el “else” aparece en la misma línea que el cuerpo de if y no es una sentencia
compuesta ( su cuerpo no va entre llaves) tenemos que separar la sentencia if de la
else con el carácter punto y coma (;).
7
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Sentencia while: imprimir los tres primeros campos de cada registro.
awk ‘{ i = 1
while (i<=3) {
printf $i
i++
}
}’
Sentencia do–while: imprimir cada registro de entrada diez veces.
awk ‘{ i=1
do {
printf $0
i++
} while (i<= 10)
}’
Sentencia for: imprimir los tres primeros campos de cada registro de entrada.
awk ‘{ for ( i=1; i<=3; i++)
printf $i
}’
Sentencia break: encontrar el divisor más pequeño de un número.
awk ‘{ num=$1
for ( div =2; div*div <= num ; div ++)
if ( num % div== 0)
break
if ( num % div == 0)
printf “ el divisor más pequeño de %d es %d”, num, div
else
printf “%d es primo”, num }’
Sentencia continue : imprimir los números de 0 a 20 excepto el 5.
awk ‘BEGIN {
x=0
for ( x=0; x<=20; x++){
if ( x==5)
continue
printf (“%d”, x)
x++
}
print “ ”
}’
Existen otras dos sentencias que son propias del lenguaje awk:
Sentencia next: fuerza a awk a que detenga el procesamiento del registro actual y
vaya a leer el siguiente. No se ejecuta ninguna regla más para el registro en curso ni
las demás acciones de la regla actual. Ejemplo:
8
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
NF != 4{
printf (“line %d skipped : doesn´t have four fields “, FNR) > “/dev/stderr”
next }
Es un ejemplo de regla que se podría utilizar al principio del programa para que no
procese entradas con más de cuatro campos.
Nota: El uso de las sentencia next no está permitido dentro de una regla begin o end.
Sentencia exit: hace que awk detenga la ejecución de la regla actual inmediatamente
y el procesamiento de la entrada. Una sentencia exit puede tener un argumento de
tipo númerico, que representa el código de estado de salida para el proceso awk. Por
defecto, este código corresponde al estado de éxito, representado con el cero.
Ejemplo: En este caso, al estar la sentencia next dentro de una regla begin, si se
produce un error, se parará todo el proceso de entrada y no se leería nada.
BEGIN {
}
if ((“date” | getline date_now)<0){
print “Can´t get system date” > “dev/stderr”
exit 4
}
•
Sentencias compuestas: consisten en una o más sentencias encerradas entre llaves.
Una sentencia compuesta tiene como cometido poner varias sentencias juntas en el
cuerpo de una sentencia if,while, do o for.
•
Control de la entrada: usando la sentencia next ya explicada y la función getline.
Función getline: permite leer la entrada bajo un control explícito establecido por el
usuario. Tiene las siguientes formas:
1. Sin argumentos: para leer la entrada del fichero de la entrada actual. En esta
caso se lee de forma normal, es decir, se procesa el siguiente registro de
entrada y se divide en campos para su análisis.
2. Con argumento “variable”: se utiliza cuando el usuario quiere leer una línea
sin que sea procesada por las reglas y patrones de awk. La línea elegida se
guarda en la “variable” y no disparará la ejecución de ninguna regla.
3. Con argumento <“fichero”: se usa cuando el usuario no quiere leer el
siguiente stream de entrada, sino la entrada de un fichero en particular, cuyo
nombre es una cadena que contiene la variable “fichero”.
•
Sentencias de salida, print y printf: son acciones que permiten sacar por pantalla
una parte o toda la entrada. La diferencia entre ellas está en que la sentencia print
utiliza un formato estandarizado y simple, mientras que printf permite al usuario
especificar el formato con el que quiere ver la salida por pantalla.
La sentencia print tiene la siguiente forma: printf item1, item2… Por ejemplo,
{print “linea uno\nlinea dos\n linea tres”} o {print $1 $2}. Simplemente se indica
la lista de parámetros a imprimir.
En cambio, la sentencia printf, incluye un parámetro llamado formato, que
representa la especificación de cómo se deben imprimir los argumentos de la lista.
El formato es esencialmente el que usa la función del mismo nombre en el lenguaje
C. Ejemplo: {printf “%-10s %s\n”, $1, $2}
9
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
7. Funciones definidas por el usuario
La definición de funciones por el usuario puede aparecer en cualquier parte entre las
reglas de un programa awk. Una función está compuesta por:
Nombre_función: es el nombre de la función que se va definir. Puede estar formado
por secuencias de reglas, dígitos y subrayados, siempre que no comience por un
dígito.
Lista_de_parámetros: lista de argumentos de las funciones y nombres de variables
locales separadas por comas. Cuando se realiza una llamada a una función, los
argumentos se utilizan para guardar los valores de los argumentos que se pasan a la
función. Las variables locales se inicializan por defecto a la cadena vacía.
Cuerpo_ de_la_función: consiste en sentencias awk. Es la parte más importante de
la definición, ya que indica lo que tiene que hacer. Los argumentos son una forma
de referirse a otras variables del programa, mientras que las variables locales se usan
para guardar valores temporales del cuerpo de la función.
Por lo tanto, la definición de una función tendrá la siguiente forma:
function nombre_función ( lista_de_parámetros){
cuerpo_de_la_función
}
Nota: los nombres de argumentos no se distinguen sintácticamente de los nombres
de las variables locales. Por ello, los primeros valores que forman la lista de parámetros
corresponderán al número de argumentos que se pasan a la función, y el resto serán
variables locales.
Una llamada a una función consiste en el nombre de la función seguido por los
argumentos entre paréntesis. Los argumentos son expresiones awk. Cada vez que se
ejecute la llamada, los argumentos se evaluarán y los argumentos que pasaremos a la
función serán los valores resultantes de dicha evaluación. Ejemplo:
Regla que llama a la función:
$3>0
Declaración de la función miprint:
{ miprint ($3) }
function miprint (num)
{
printf “%6.3g”, num
}
Nota: no se permiten espacios en blanco entre el nombre de la función y el
paréntesis de apertura de la lista de parámetros, ya que si hubiera algún tabulador o
espacio, awk podría pensar que se desea concatenar la lista de argumentos con una
variable.
10
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
8. Arrays en awk
Un array es una tabla de varios valores, llamados elementos. Los elementos se
distinguen por sus índices, que pueden ser tanto cadenas como números. Cada array
tiene un nombre, que no puede ser igual al nombre de una variable que se esté usando
actualmente.
El lenguaje awk posee arrays de una dimensión para almacenar grupos de cadenas o
números relacionados entre sí. Su funcionamiento es similar al de los vectores de otros
lenguajes de programación, pero con una diferencia significativa: en awk no hace falta
indicar el tamaño del vector antes de empezar a usarlo. En los otros lenguajes, es
necesaria una reserva previa de memoria para empezar a trabajar con un array. En
cambio, en awk los vectores son asociativos o colecciones de pares compuestas por un
índice y su valor del elemento del vector correspondiente. El orden de los mismos no
tiene ningún significado. Ejemplo:
Si tenemos el siguiente array de cuatro elementos: [ 8 foo “”
30 ], en awk
podemos referenciar cada uno de sus elementos de la siguiente manera:
Elemento 4
Elemento 2
Elemento 1
Elemento 3
Valor 30
Valor “foo”
Valor 8
Valor “”
Una de las ventajas de los arrays asociativos es que se puede añadir un nuevo
elemento siempre que se quiera. Ene el ejemplo anterior, podemos perfectamente añadir
un elemento décimo cuyo valor sea la cadena “número diez”.
Otra ventaja es que los índices no tiene que ser enteros positivos, pudiendo una
posición del vector ser referenciada por una cadena (por ejemplo, “one”) e incluso por
una cadena y un número entero. Ejemplo:
Elemento “one” Valor “un”
Elemento 1
Valor “un”
Para acceder a un elemento de un array, basta con poner el nombre del array seguido
entre corchetes de la posición elegida: array [índice]. En el caso de que esa posición esté
vacía, se devuelve la cadena nula.
Si queremos recorrer todos los elementos de un vector, sólo tenemos que hacer un
bucle for de la siguiente forma:
for ( variable in array)
cuerpo
for (i=1;i<=NF;i++)
used[$i]=1;
Si por el contrario se quiere borrar un elemento de un array, se usará la sentencia
delete: delete array [index]. Cuando un elemento es eliminado es como si nunca se
hubiera referenciado y nunca se le hubiera dado un valor. Por lo tanto, si se trata de
referenciar, el programa devolverá la cadena nula.
11
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Awk también posee arrays multidimensionales, que funcionan de la misma forma,
salvo porque tienen que ser referenciado por tantos índices como dimensiones se quiera
que tenga el vector ( por ejemplo, un array bidimensional necesitará dos índices para ser
referenciado).
9. Programas de ejemplo
Ejecución de programas:
Como ya se ha dicho antes en el apartado de sintaxis, para ejecutar awk utilizaremos
la siguiente sintaxis:
Con esto podremos ejecutar los programas de dos formas: A través de la línea de
comandos (omitiendo “-f archivo_programa”), o bien a través de un script de awk
(omitiendo “’programa’”).
La primera forma, a través de la línea de comandos, nos fuerza a declarar todo el
programa en la línea de comandos, encerrándole entre comillas simples. Bueno para
programas cortos. La sintaxis quedaría así:
La segunda, es más útil cuando los programas son extensos, y es escribiendo el
programa en un archivo de texto (basta con utilizar el block de notas o el editor vi). La
sintaxis sería así:
Para definir la entrada del programa, podremos pasarle un archivo que queramos
procesar. Si no le pasamos ningún archivo, procesará la entrada estándar.
Existe otra forma de ejecutar awk en algunos sistemas UNIX, y es mediante scripts
de la shell. Para ello, podemos usar el mecanismo de shell ‘#!’. Por ejemplo, el script
“hola” con el siguiente contenido:
#! /bin/awk –f
BEGIN {print “Hola”}
al ser ejecutado, se comportaría como si hubiésemos ejecutado:
awk –f hola
(recordad que el símbolo ‘#’ se usa para definir un comentario en awk, luego el
archivo anterior sigue siendo un script de awk válido)
12
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Veamos algunos programas de ejemplo.
Mostrar la suma total de los tamaños de los archivos modificados por última vez en
noviembre
ls –l | awk ‘$6 == “Nov” { sum += $5 } END { print sum }’
El programa, genera el listado de archivos en la carpeta actual, pasándosela
como parámetro a AWK.
AWK entonces comparará el 6º campo (mes de la última modificación) con la
cadena de caracteres “Nov”. Si se cumple la igualdad, ejecutará la siguiente acción
(incrementar sum en el valor del 5º campo –tamaño del archivo).
Por último, cuando no queden más registros que analizar (END), ejecutará la
acción: Mostrar por pantalla el valor de la variable sum –suma del tamaño de todos
los archivos que fueron modificados por última vez en Noviembre.
Programa que repita todos los registros que contengan la subcadena “th”
awk ‘/th/’
Este programa analiza los registros buscando el patrón /th/, y ejecuta la acción
por defecto { print $0 } que muestra el registro completo.
El patrón viene definido por una expresión regular, que se encuentra entre las
dos barras /.../. Cuando un registro cumpla este patrón, se ejecutará la acción.
Así, para la entrada:
Cathy
Tom
William
Thomas
The car is stopped
AWK mostrará:
Cathy
Thomas
The car is stopped
Programa que muestre los campos 1 y 2 de una lista y les muestre separados por “;”
y con una línea en blanco entre ellos
awk ‘BEGIN { OFS=”;”; ORS=”\n\n” }
{ print $1, $2 }’ Lista-BBS
Cuando ejecutemos AWK, se llamará a la acción asociada a BEGIN. Ésta,
cambiará los valores por defecto de OFS y ORS (“ “ y “\n” respectivamente), para
cambiar el Output Field Separator –Separador de Campos de Salida- a un “;”, y el
13
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Output Registry Separator –Separador de Registros de Salida- a un “\n\n” (nueva
línea y línea en blanco).
Después, para todo registro leído (no tiene asociado ningún patrón), se mostrarán
los campos 1º y 2º. La coma en el print indica que entre ambos campos deberá
colocar un OFS, y al terminar print colocará un ORS. Luego nuestra salida tendrá
los campos separados por un “;” y con una línea en blanco entre ellos.
Programa que muestre una lista en una tabla formateada de dos columnas
awk ‘BEGIN {printf “%-10s %s\n”, “Name”, “Number”;
printf “%-10s %s\n”, “----“, “------“
}
{printf “%-10s %s\n”, $1, $2}’ Lista-BBS
Al ejecutar AWK, la acción BEGIN imprimirá el encabezado de la tabla.
Después, para todo registro leído, mostrará los campos 1º y 2º con justificado, de
modo que los campos quedarán alineados.
Programa que muestra los registros en un fichero en orden inverso
awk ‘{
a[NR] = $0
}
END {
for (i = NR; i>0; --i)
print a[i]
}’
El programa utiliza el valor de la variable NR para identificar el número de
registro (en este caso el número de línea), y almacenar dicho registro en esa posición
dentro de un array (almacena los registros en memoria)
Una vez ha terminado de leer todo el fichero de entrada (END), le muestra por
pantalla recorriendo el array en sentido contrario.
Programa que muestre ordenada una lista desordenada
awk ‘{if ($1>max)
max=$1 arr[$1]=$0
}
END {
for (x=1; x<=max; x++)
print arr[x]
}’
El programa recibe como entrada una lista con dos campos: La posición dentro
de la lista, y el contenido de la lista en esa posición.
14
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Recorre todos los campos. Al no tener un patrón, la acción se ejecuta para cada
registro leído. Lo que hace entonces es: En primer lugar, si es la posición más
“alejada” que ha encontrado hasta ahora, actualiza la posición máxima. Entonces,
pone el valor del array en la posición que indique el campo 1 (posición en la lista)
con el valor de todo el registro.
Una vez haya rellenado todo el array (no quedan registros por leer), se ejecuta la
acción asociada a END, que es un bucle que muestra el contenido de esa lista/array.
Para la entrada:
4 A mí me ha tocado el cuarto
2 Y yo el segundo
5 Y yo el último
3 Pues yo el tercero
1 Soy el primero
generará la salida:
1 Soy el primero
2 Y yo el segundo
3 Pues yo el tercero
4 A mí me ha tocado el cuarto
5 Y yo el último
Programa que recibe números y muestre los mayores de 0 alineados a 6 caracteres a
la derecha y con tres dígitos para la parte decimal
awk ‘function miprint(num) {
printf “%6.3g\n”,num
}
$1>0 {miprint($1)}’
Primero definimos la función miprint(num), que recibe un número, y le
imprimirá formateado usando printf.
Después, para todo registro mayor que 0, llamará miprint, pasándole dicho
número como parámetro, mostrando así el número con ese formato.
Cálculo de factoriales de números naturales con awk
awk ’function fact(num) {
if (num <= 1)
return 1
else
return num * fact(num - 1)
}
{ print $0 " factorial is " fact($0) }’
El programa define la función fact() que calcula el factorial de un determinado
número recursivamente. Para cada registro de entrada, calculará su factorial.
15
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
10. AWK AVANZADO
1. Referenciando campos sin usar constantes:
Como ya dijimos en el manual básico, cuando un programa awk lee un registro de
entrada, éste se divide en campos. Dichos campos son referenciados mediante el
símbolo $ y un número entero, que corresponde a la posición que ocupa respecto al
registro.
Sin embargo, estos campos pueden ser también referenciados por cualquier
expresión del lenguaje. El valor de dicha expresión especificará el número del campo.
En el caso de que sea una cadena, ésta se convertirá a número. Ejemplo:
awk ‘ { print $(2*2)} ‘ Lista_BBS
En este caso, la expresión 2*2 se evaluará para obtener el valor del campo deseado
( que será el campo número 4).
El uso de paréntesis tiene una gran importancia, ya que indican que la evaluación de
la expresión numérica se tiene que realizar antes que la operación $.
Si el número de campo toma el valor 0, se obtendrá el registro entero. Los números
de campo negativos no se permiten.
2. Cambiando los contenidos de un campo:
Asimismo, los contenidos de un campo se pueden cambiar. Pero cuidado, awk no
cambiará el fichero de entrada original. Ejemplo:
awk ‘{ $3=$2-10; printf $2,$3}’ inventario
El signo – representa la sustracción, de modo que este programa reasigna al valor
tres el resultado de evaluar la expresión $2-10, imprimiendo los nuevos valores de los
campos al final.
Para poder realizar esta operación, el contenido de un campo deber ser numérico, o
en el caso de ser una cadena, susceptible de ser convertida a número. El valor resultante
de la evaluación de las expresiones se convierte otra vez a cadena cuando se asigna a un
campo.
Una consecuencia del cambio de los contenidos de un campo es que el texto del
registro de entrada es recalculado para obtener el nuevo campo en la posición en la que
estaba el antiguo. Por lo tanto, $0 cambiará para reflejar la alteración. Ejemplo:
awk ‘{$2=$2-10; printf $0}’ inventario
En este programa se imprime una copia del fichero de entrada restándole 10
unidades a cada uno de los valores que representa el campo número dos para todas las
líneas.
16
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
3. Campos fuera de rango:
Otra operación interesante con los campos es dar valores a campos que estén fuera
del rango. Ejemplo:
awk ‘ {$6 =($5+$4+$3+$2); print $6}’ inventario
Acabamos de crear un nuevo campo $6, cuyo valor asociado es el resultado de la
suma de otros campos .
La creación de un nuevo campo cambia la copia interna del registro de entrada
actual (el valor $0).Por lo tanto, si ahora imprimimos el valor de dicho campo, se
añadirá el nuevo campo creado. Esta recomputación afecta sobre todo al separador de
campos de salida, OFS, que tendrá que añadir nuevos separadores entre el registro
nuevo y los previos. Otro valor que se ve afectado es el NF o número de campos que se
fijará al mayor valor de los campos nuevos que se hayan creado.
Sin embargo, el valor que devuelve awk cuando se referencia a un campo fuera del
rango no cambia ( sigue devolviendo la cadena nula). Ejemplo:
if( $(NF+1) !=” ”)
print “no puede ocurrir”
else
print “ todo es normal”
Se imprimirá la cadena “todo es normal” ya que NF+1 está fuera de rango.
4. Utilizando pipes:
Un pipe es una forma de enlazar la salida de un programa con la entrada de otro. En
awk se suele utilizar sobre todo para modificar la forma de leer la entrada. Ejemplos:
awk ‘{
if ($1= = “@execute”){
tmp = substr ($0,10)
while((tmp | getline)>0)
print
close (tmp)
}else
print
}’
Este programa copia la entrada a la salida, excepto las líneas que comienzan por
@execute, las cuales son reemplazadas por la salida producida por la ejecución del
resto de la línea como un comando de shell.
Obsérvese que tmp se ejecuta como un comando de shell y su salida es pipeada
dentro de awk para que sea usada como entrada.
La función close se usa para asegurarnos que de que si aparecen dos líneas
@execute idénticas, el comando se ejecute de nuevo para cada línea.
awk ‘BEGIN {
17
Lenguaje awk
}’
Jesús Alonso Abad
Estrella Resa Camarero
“date” | getline tiempo_actual
close (“date”)
printf “Report printed on” tiempo_actual
El programa lee la hora y el día actual a la variable tiempo_actual, usando la
utilidad date, y después la imprime.
5. Redireccionando la salida de un programa awk:
El resultado de la ejecución de las sentencias print o printf se puede mandar a un
sitio que no sea la pantalla. Hay tres formas diferentes de redireccionar la salida:
* print items > fichero salida: este tipo de redirección imprime los items en un
fichero de salida en el que previamente se ha destruido todo su contenido previo. Si
dicho fichero no existe, se crea. Ejemplo:
awk ‘{printf $2 > “lista-teléfonos”
printf $1 > “ lista-nombres”}’ Lista-BBS
Este programa escribe una lista de nombres en un fichero de nombres y una lista de
teléfonos en un fichero homónimo. Cada fichero de salida contendrá un nombre o un
número de teléfono por línea.
*print
items >> fichero salida: la diferencia con la forma anterior es que el
contenido previo del fichero no se destruye, sino que los nuevos items leídos se añaden
al final del mismo.
*print items | comando: este tipo de redireccionamiento abre un pipe a comando y
escribe los valores de items a través de ese pipe. Ejemplo:
awk ‘{ print $1 > “names. unsorted”
print $1 | “sort –r > names.sorted” }’ Lista-BBS
Este programa produce dos ficheros: una lista sin ordenar de nombres y una lista
ordenada en orden alfabético inverso. La lista desordenada se escribe con una
redirección normal, mientras que la ordenada se escribe con un pipe al comando sort
de Unix ( dicho comando es el que realiza la acción de ordenar).
6. Cerrado de ficheros de salida y pipes:
En un programa de ejemplo, anterior, ya hemos ilustrado cómo se ejecuta la función
close, donde se utilizaba para volver a ejecutar un comando. Dicha función también se
puede utilizar para cerrar ficheros de salida y pipes.
La sintaxis es: close ( nombre-fichero) o close (comando). Por ejemplo, si hemos
abierto este pipe print $1 | “sort –r > names.sorted” deberemos cerrarlo con lo
siguiente close (“sort –r > names.sorted”). Es decir, el valor del argumento de la
función close debe coincidir con la cadena usada para abrir el pipe o el fichero.
18
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
Las ocasiones en que vamos a tener que cerrar un fichero de salida son bastante
numerosas, ya que para poder leerle tenemos antes que cerrarle, y no podemos dejar
abiertos un número indeterminado de ficheros dentro del mismo programa awk, ya que
podríamos exceder el límite del sistema de ficheros abiertos por proceso. Otro caso en el
que debemos cerrarle es cuando volvemos a ejecutar el mismo programa varias veces
con los mismos argumentos.
Un pipe se debe cerrar para que un comando acabe. Mientras el pipe esté abierto, el
comando intentará leer la entrada del pipe. Así pues, para que el comando lleve a cabo
su trabajo, es conveniente cerrarle. Por ejemplo, si redireccionamos la salida a un
programa mail, el mensaje realmente no se envía hasta que el pipe haya sido cerrado.
7. Funciones implícitas
Awk cuenta con numerosas funciones implícitas (incluidas en el propio lenguaje).
Se llaman de la misma manera que las funciones definidas por el usuario. Aquí se ofrece
una lista con las funciones implícitas de awk y su descripción:
Numéricas:
int(x)
Sqrt(x)
Exp(x)
log(n)
sin(x)
Cos(x)
Atan2(y, x)
rand()
srand(x)
time()
Retorna la parte entera de x redondeada hacia abajo
Raíz cuadrada positiva de x (x no puede ser negativo)
Exponencial de x
Logaritmo natural de x
Seno de x (x en radianes)
Coseno de x
Cotangente de y/x
Número aleatorio entre 0 y 1
Coloca la semilla de aleatoriedad (el valor que usará como referencia para
generar los números aleatorios) con el valor de x.
Si se omite x, utiliza la fecha y hora del sistema (para conseguir la
máxima aleatoriedad).
Devuelve la hora actual en segundos desde el 1-1-1970
Cadenas:
index(cadena,
subcadena)
Devuelve la posición de subcadena dentro de cadena. La
posición inicial es 1. Devuelve 0 si subcadena no está
contenida en cadena.
length(cadena)
Devuelve la longitud de la cadena. Si se omite cadena
devuelve la longitud de $0
match(cadena, expreg) Función similar a index(), salvo que en esta ocasión, la
subcadena viene definida por una expresión regular expreg.
Devuelve la posición en la que se encuentra la subcadena más
larga y más a la izquierda que concuerda con la expresión
regular, o 0 si no encuentra ninguna.
Nota: awk cuenta con dos variables implícitas, RSTART y
RLENGTH, donde match almacenará los valores de la posición
y longitud de la subcadena respectivamente (el valor que
devuelve match coincidirá con el de RSTART)
split(cadena, vector,
Divide cadena en subcadenas, usando separador para
separador)
dividirlas, y almacenando los fragmentos en vector. Así,
19
Lenguaje awk
sprintf(...)
sub(expreg, nueva,
cadena)
gsub(expreg, nueva,
cadena)
substr(cadena, inicio,
longitud)
tolower(cadena),
toupper(cadena)
Jesús Alonso Abad
Estrella Resa Camarero
split(“30/11/2004”, v, “/”) generará los vectores v[1]=”30”,
v[2]=”11”, v[3]=”2004”.
Similar a printf, pero esta vez la salida generada saldrá en
forma de cadena, en vez de ser mostrada por pantalla.
Sustituye la subcadena más larga y más a la izquierda que
concuerde con expreg dentro de cadena por nueva. cadena
debe ser una variable (no sirven cadenas literales). Si se omite
cadena, utilizará $0.
Se puede usar el caracter especial ‘&’ si se quiere incluir de
nuevo la subcadena encontrada (sustituirá ‘&’ en nueva por la
subcadena). Si se quiere incluir un caracter ‘&’ dentro de la
salida en vez de sustituirle por la subcadena, hay que poner una
barra invertida antes (para poner esta barra invertida, es
necesario ponerla como caracter literal).
sub(/yo/, “mis amigos y &”) convertirá “Sólo yo” en “Sólo mis
amigos y yo”
sub(/y/, “\\&”) convertirá “Tú y él” en “Tú & él”
Similar a sub(), pero se sustituirán todas las subcadenas que
concuerden con expreg
Devuelve la subcadena de cadena que comience en inicio con
tamaño longitud. Si se omite longitud, devuelve el sufijo de
cadena que comience en inicio. La posición de la primera letra
es 1. Así, substr(cadena, 1) devuelve cadena
Convierte
cadena
a
minúsculas
y
mayúsculas,
respectivamente.
Entrada/salida:
system(comando_de_sistema) Ejecuta el comando de sistema. Ejemplo: system(“ls –
l”). Retorna el valor devuelto por el programa ejecutado.
8. Arrays multidimensionales
Awk es capaz de trabajar con arrays multidimensionales (por ejemplo, tablas),
convirtiéndoles a vectores. La forma de hacerlo es sencilla: awk convierte los índices a
cadenas. Así, vector[5] es sinónimo de vector[“5”]. En el caso de usar varios índices
(arrays multidimensinales), utiliza la variable implícita SUBSEP, convirtiendo las
comas utilizadas para separar los índices en caracteres/cadenas indicados en SUBSEP.
Por ejemplo, si SUBSEP=”:”, vector[3,2] será interpretado como vector[“3:2”], de
manera que sea un vector unidimensional, con un identificador único.
El valor por defecto de SUBSEP es “\034”, carácter no imprimible, y muy poco
probable que aparezca en un programa awk o en los datos de entrada. Se hace así
porque se pueden producir ambigüedades al fusionar los índices. Por ejemplo, si
SUBSEP=”:”, vector[“1:2”, “3”] es el mismo elemento que vector[“1”, “2:3”], porque
ambas apuntan a vector[“1:2:3”], mientras que con SUBSEP=“\034” es muy difícil que
se dé este caso.
20
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
9. Variables implícitas que proporcionan información a un programa
ARGC, ARGV: los argumentos de la línea de comandos disponibles son
almacenados en un array llamado ARGV. ARGC es el número de argumentos de
lína de comandos presentes. ARGV está indexado desde 0 hasta ARGC-1. Ejemplo:
awk ‘{ print ARGV [$1] }’ inventario-enviado Lista-BBS
En este ejemplo, ARGV [0] contiene “awk”, ARGV[1] contiene“inventarioenviado”, y ARGV[2] “Lista-BBS”. El valor de ARGC es tres, uno más que el índice
del último elemento de ARGV ya que el primer elemento es el cero.
Dentro de un programa se pueden alterar los valores de estas dos variables. Cada
vez que awk alcanza el final de un fichero de entrada, utiliza el siguiente argumento de
ARGV como el nombre del siguiente fichero de entrada. Almacenando una cadena
distinta es la variable, se pueden cambiar los ficheros que van ser leídos por el
programa. Almacenando elementos adicionales e incrementado ARGC podríamos leer
ficheros adicionales.
Si decrementamos el valor de ARGC, eliminaremos ficheros de entrada a procesar.
Si no queremos leer un fichero que se encuentra a la mitad de la lista de ficheros a
procesar, almacenaremos la cadena nula (“”) en ARGV en lugar de ese nombre de
fichero. De esta forma, awk le ignorará.
ENVIRON: array que contiene las variables de entorno. Los índices del array son los
nombres de las variables de entorno. Por ejemplo, ENVIRON{“HOME”} podría ser
‘/home/dwadmin’. El cambio de valores de este array no afecta para nada al valor
real de las variables de entorno del sistema operativo.
Si el sistema operativo no tuviera variables de entorno, este array estará vacío.
FILENAME: nombre del fichero que awk está leyendo en la actualidad. Si awk está
leyendo de la entrada estándar, esta variable toma el valor de “-“.
RLENGTH: es la longitud de la cadena encontrada por la función match (explicada
en el apartado de funciones implícitas) . Su valor es la longitud de la cadena
encontrada o por defecto -1 si no encontró ninguna cadena.
RSTART: índice de comienzo de la subcadena encontrada por la función match. Su
valor es la posición de la cadena donde comienza la subcadena o 0 si no se encontró.
10. Precedencia y anidación de operadores en awk
En este capítulo describiremos cuáles son los operadores que se utilizan en awk, así
como el uso su orden de precedencia.
Hay varios tipos de operadores en awk: están los puramente aritméticos (suma,
resta, módulo…), los operadores lógicos, los operadores relacionales, los operadores de
patrón, incremento…
La precedencia de operadores determina cómo se agrupan los mismos cuando
aparecen distintos operadores cerca unos de otros en una expresión.
Si los operadores son unarios, la preferencia no importa, ya que afectan sólo a un
único elemento. Por ejemplo, si tenemos $++i, es lo mismo que $(++i). Sin embargo, si
21
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
otro operador sigue al unario, se pueden dar problemas, por lo que –x**2 significa
–(x**2) y no (-x)**2, ya que el operador sustracción o resta tiene menor precedencia
que el operador de la multiplicación.
De todas formas, la precedencia de operadores se puede cambiar mediante los
paréntesis. De hecho se recomienda el uso de paréntesis cuando se tiene una
combinación inusual de operadores para que la secuencia de operaciones sea más fácil
de entender para los usuarios.
Si se usan juntos operadores de igual precedencia, el que esté situado más a la
izquierda es el que tiene mayor predecencia. Sin embargo, en el caso de los operadores
asignación, exponenciación y condicionales se agrupan en el orden contrario. Ejemplos:
a- b +c
(a- b) +c
a=b=c
a = (b = c)
A continuación presentamos una lista de los operadores de awk en orden de
precedencia creciente:
Asignación
Condicional
“o” lógico
“y” lógico
Pertenencia a array
Concordancia o patrón
Relacional y direccionamiento
Concatenación
Adición, sustracción
Multiplicación, división y módulo
Más y menos unario, operador not
Exponenciación
Incremento, decremento
Campo
‘=’,’+ =’,’- =’,’* =’,’/=’, ‘% =’, ‘^ =’,
‘** =’
Estos operadores se agrupan de derecha
a izquierda.
‘?:’
Estos operadores se agrupan de derecha
a izquierda
‘||‘
‘ &&’
in
‘ ~’ , ‘! ~’
Operadores relacionales: ‘<’, ‘>’, ‘<=’,
‘>=’, ‘= =’, ‘!=’
Operadores
de
redireccionamiento
entrada-salida: ‘<’,’>’,’>>’,’|’.
Los operadores relacionales y las
redirecciones tienen el mismo nivel de
precedencia.
No se usa ningún token especial para
indicar esta operación.
‘+’, ‘-‘
‘*’, ‘/’, ‘%’
‘+’,’-‘,’!’
‘^’,’**’
Estos operadores se agrupan de derecha
a izquierda
‘+ +’, ‘--‘
‘$’
11. Portabilidad: awk compilado
Existen numerosas herramientas de conversión y de compilación de awk, que
permiten un mejor rendimiento y portabilidad, en el sentido de que deja de ser necesario
tener instalado awk en la máquina. Ejemplos de estas herramientas son awka
22
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
(http://awka.sourceforge.net/), proyecto open-source que genera código fuente ANSI-C
a partir de un script awk, que es posteriormente compilado por gcc o cualquier otro
compilador
de
lenguaje
C,
o
la
herramienta
comercial
awkc
(http://www.mkssoftware.com/docs/man1/awkc.1.asp), que compila directamente el
código fuente de un script awk. Existen muchas más herramientas de este tipo
disponibles en la red.
23
Lenguaje awk
Jesús Alonso Abad
Estrella Resa Camarero
12. Bibliografía y referencias
Sed & Awk. Por Dale Dougherty & Arnold Robbins. O’Reilly. 1997
Manual de awk. Por Jesús Alberto Vidal Cortés. Edición del 2002
http://inicia.es/de/chube/Manual_Awk/Menus.htm
Ejemplos con awk: una breve introducción. Por Jesús Palacios Bermejo
http://www.linuxfocus.org/Castellano/September1999/article103.html
Resumen del lenguaje awk
http://club.telepolis.com/jagar1/Unix/Awk.htm
Curso de awk Rodolfo Pilas (apuntes de una conferencia impartida por Heber
Godoy)
http://www.linux.org.uy/uylug/cursos/awk/awk.html
awk – data transformation, report generation language
http://www.mkssoftware.com/docs/man1/awk.1.asp
Introduction to awk. Por Brian Brown, CIT
http://allman.rhon.itam.mx/dcomp/awk.html
Awk language programming
http://snap.nlc.dcccd.edu/reference/awkref/gawk_toc.html
Sección sobre Awk en “La web del programador”
http://www.lawebdelprogramador.com/cursos/mostrar.php?id=174&texto=AWK
24
Descargar