iberck
10/12/2017 - 7:12 PM

awk

awk programming

awk 1 line

awk 'BEGIN{print "hola";}'

awk N lines

awk ' (ENTER)
> BEGIN {
>  print "one"
> }' (ENTER)

Procesar un archivo awk

awk -f process.awk

Procesar un archivo awk con un archivo de texto como entrada

awk -f process.awk file_input

La condición y el inicio de su acción deben estar en la misma línea, no pueden estar separadas. En caso de estar separadas hay que escapar con un backslash \:

/onActionFromEdit/ \
{
  printf "[%s] %s\n",FILENAME,$0;
}

# Otra manera
/onActionFromEdit/ {
  printf "[%s] %s\n",FILENAME,$0;
}

Procesar un archivo csv

awk -F,: Indica que el separador de columnas es la ,

Los archivos csv exportan todos los valores con comillas, con gsub(/"/,"",$0); se quitan:

awk -F, '{gsub(/"/,"",$0); print $2}' values.csv

Procesando la salida de un comando

awk también puede realizar un procesamiento de texto sin un archivo de texto de por medio, en el siguiente código recoge el texto de otro comando:

echo "carlos" | awk '{print "hola", $0 }'

Procesar todos los archivos de un directorio

Forma 1:

awk -f process.awk *

Forma 2: process.sh:

for f in *; do
  awk '/pattern/ {print}' $f
done

procesar más de un archivo a la vez

awk '{print $0)' file1.txt file2.txt: primero procesa file1.txt y cuando termina procesa file2.txt

NR==FNR{print "primer archivo:",$0}: condición para saber si se está procesando el primer archivo.

NR!=FNR{print "segundo archivo:",$0}: condición para saber si se está procesando el segundo archivo.

Estructura

awk 'BEGIN {action}
     condition {action;}
    END {action} input_file'

BEGIN{}: Se ejecuta antes de procesar el archivo

condition: En este espacio acepta una condición (exp booleana), si evalúa a true ejecutará el cuerpo {}. Por ejemplo, si solo quisieramos que se escriba la tercer fila: NR==3{print "tercer fila:",$0} file1.txt.

Si se envía una expresión regular /search/ {}, se ejecuta cada que una línea hace match con /search/

Si no tiene condición evalúa a true y se ejecuta la acción.

Pueden existir N condiciones, cada una con su respectiva acción.

{}: la acción por defecto es print

END{}: Se ejecuta cuando termina de procesar el archivo

input_file: Archivo a procesar (solo se puede enviar 1 archivo, no acepta directorios)

Columnas

$0: Toda la línea

$1: Primer columna

$2: Segunda columna

$3: Tercer columna

$NF: Última columna

Strings

Las cadenas se representan SIEMPRE con "".

En awk no se utiliza un operador para concatenar cadenas:

i=9; print "hola ("i") mundo"

String functions - ejemplos simples

Replace

gsub: global substitution sirve para hacer un remplazo de todas las coincidencias

gsub(/regex-find/,replacement, string);: busca la regex y lo remplaza con replacement en el string indicado (puede ser una columna).

gsub(",","",$0); print $0: Quita las comas de toda la línea

text="hola mundo nombre,nombre";
gsub("nombre", "carlos", text);
print text;

Output: hola mundo carlos,carlos

sub: Funciona igual que gsub pero solo remplaza la primer ocurrencia encontrada de izquierda a derecha.

text="hola mundo nombre,nombre";
sub("nombre", "carlos", text);
print text;

Output: hola mundo carlos,nombre

split

str = "One,Two,Three,Four"
split(str, arr, ",")
print "Array contains following values"

for (i in arr) {
  print arr[i]
}

print

Por automático agrega un salto de línea al final

# Ambos imprimen toda la línea
print
print $0
world="mundo"
name="carlos"
print "hola " world " de " name

# prints: hola mundo de carlos
print "hola","carlos"`: imprime `hola carlos` (la `,` agrega por automático el espacio)

printf

Sirve para imprimir una cadena formateada (texto/fecha/número/hora/moneda). Por defecto no agrega un salto de línea al final.

printf "hola (%s) mundo\n",5

Separador de columnas

Por defecto el separador de columnas es:

  • 1 espacio en blanco
  • múltiples espacios en blanco
  • tabulador

awk -F| ...: Cambiar el separador de columnas por defecto por |:

Comentarios

# comentario

Tipos de datos

El valor de una expresión es siempre un Entero o un String.

Some contexts (such as arithmetic operators) require numeric values. They convert strings to numbers by interpreting the text of the string as a number. If the string does not look like a number, it converts to zero.

Other contexts (such as concatenation) require string values. They convert numbers to strings by effectively printing them with sprintf. See section Conversion of Strings and Numbers, for the details.

To force conversion of a string value to a number, simply add zero to it. If the value you start with is already a number, this does not change it.

To force conversion of a numeric value to a string, concatenate it with the null string.

Comparisons are done numerically if both operands are numeric, or if one is numeric and the other is a numeric string. Otherwise one or both operands are converted to strings and a string comparison is performed. Fields, getline input, FILENAME, ARGV elements, ENVIRON elements and the elements of an array created by split are the only items that can be numeric strings. String constants, such as "3.1415927" are not numeric strings, they are string constants. The full rules for comparisons are described in section Variable Typing and Comparison Expressions.

Uninitialized variables have the string value "" (the null, or empty, string). In contexts where a number is required, this is equivalent to zero.

Boolean

Como solo existen enteros y Strings:

0: false

1: true

Por ejemplo:

$0~/ERROR/ # Retorna 1 si hace match, 0 si no hace match

regex search patterns

/regex/ La línea $0 CONTIENE la regex

/sa/: La línea $0 CONTIENE sa.

$0 ~ "sa": La línea $0 CONTIENE sa (funciona igual que la línea anterior)

$0 ~ /sa/: La línea $0 CONTIENE sa (funciona igual que la línea anterior)

$0 ~ /"sa"/: La línea $0 CONTIENE sa (funciona igual que la línea anterior)

$0 !~ /sa/: La línea $0 NO CONTIENE sa

/^sa/: La línea $0 COMIENZA con sa.

$0 !~ /^sa/: La línea $0 NO COMIENZA con sa

$0 == "[DEBUG] AbstractControlNode - [ENTER] onActionFromEdit": La línea ES EXACTAMENTE IGUAL a la cadena

$0 ~ /\[DEBUG\] AbstractControlNode \- \[ENTER\] onActionFromEdit/: La línea CONTIENE LA CADENA (es necesario escapar los caracteres especiales de la regex)

string search patterns

index($0, "[DEBUG] AbstractControlNode - [ENTER] onActionFromEdit")!=0: La línea CONTIENE LA CADENA (la cadena no es una regex por lo que no se tiene que escapar)

index($0, "[DEBUG] AbstractControlNode - [ENTER] onActionFromEdit")==0: La línea NO CONTIENE LA CADENA (la cadena no es una regex por lo que no se tiene que escapar)

string find index

match("busca aqui (regex)", "usca"): Retorna el índice de el primer match de la regex en el string str. Retorna 0 si no encuentra un match. Los índices comienzan en 1, en el ejemplo retorna 2.

Map (arrays asociativos)

Asignar un valor:

map["key"]=value

recorrer el mapa:

for(key in map) {
    value=map[key];
}

Busca si la llave está dentro del mapa:

if (key in map){}

Remove element:

delete map["key"];

Arrays

Regularmente los arrays en los lenguajes de programación son de tamaño fijo y con índices contiguos(0,1,2,...). En awk los arrays son asociativos (mapas), es decir, los índices pueden ser enteros o strings y si son enteros no tienen por qué ser contiguos. Por convensión, los arrays que tienen índices enteros empiezan desde el índice 1.

Recorrer un array:

for (idx in array){
  value = array[idx];
}

Validar si existe un índice dentro del array:

if (indx in array)

Obtiene el tamaño del array:

function len(a) {
    i=0;
  for(k in a){
    i++;
  }
  return i;
}

Eliminar un elemento del array:

delete foo[4];

Sort in awk

Las funciones que existen en awk para hacer sort tienen un comportamiento un tanto "extraño" ya que sort en sus 3 versiones ordena los valores de un array asociativo pero destruye los índices (llaves), mientras que sorti ordena los índices del array asociativo y luego los pasa a los valores destruyendo en este caso los valores antiguos.

La manera más natural de ordenar elementos en awk es a través de PROCINFO["sorted_in"] ya que en vez de ordenar y modificar el array, se indica la manera en la que awk recorrerá todos los arrays asociativos en los for(i in array){}.

asort (by value)/asorti (by index)

asort: Esta función ordena los valores del array. Por defecto toma los valores como String o como Entero dependiendo del tipo de dato definido en el valor.

asorti: Esta función ordena los índices del array. Por defecto toma los índices como fueran Strings (no importa que sean enteros), por lo tanto ordena los índices como cadenas: 1 10 3.

A continuación se describen las diferentes versiones de asort/asorti, ambas tienen 3 versiones con los mismos argumentos, sin embargo por simplicidad solo se explica las funciones de asort.

asort(a): Ordena los valores del array a y remplaza los índices con enteros secuenciales empezando por 1:

a["last"]="de";
a["first"]="sac";
a["middle"]="cul";
asort(a);

OUTPUT
a[1] = "cul";
a[2] = "de";
a[3] = "sac";

asorti(a): Ordena los índices del array a y los pasa a los valores, remplaza los índices con enteros secuenciales empezando por 1:

a["last"]="de";
a["first"]="sac";
a["middle"]="cul";
asort(a);

OUTPUT:
a[1] = "first";
a[2] = "last";
a[3] = "middle";

asort(src, dest): Funciona igual que la versión asort(a), sin embargo las modificaciones las realiza sobre el array dest dejando intacto el array original src:

asort(src, dest, how): Funciona igual que la versión asort(src, dest) sin embargo el tercer argumento how sirve para indicar la función con la cuál ordenar, a continuación se describen las funciones predefinidas de ordenamiento en awk:

Todas las versiones retornan el número de elementos dentro del array que se desea ordenar

PROCINFO["sorted_in"]

Instrucción de awk que sirve para recorrer el array de forma transversal, es decir a través de esta instrucción se indica a awk la manera en la que se recorrerán de manera global los arrays

A continuación se describen todos los valores que puede tomar PROCINFO["sorted_in"]:

@val_type_asc (* función default): Ordena los valores del array de acuero al tipo de dato del valor ( order ASC)

@val_type_desc: Ordena los valores del array de acuero al tipo de dato del valor ( order DESC)

@val_str_asc: Ordena los valores del array tratándolos como Strings (order ASC)

@val_str_desc: Ordena los valores del array tratándolos como Strings (order DESC)

@val_num_asc: Ordena los valores del array tratándolos como Integers (order ASC)

@val_num_desc: Ordena los valores del array tratándolos como Integers (order DESC)

@ind_str_asc (* función default): Ordena los indices del array tratándolos como Strings (order ASC)

@ind_num_asc: Ordena los indices del array tratándolos como Integers ( order ASC)

@ind_str_desc: Ordena los indices del array tratándolos como Strings (order DESC)

@ind_num_desc: Ordena los indices del array tratándolos como Integers ( order DESC)

Ejemplo:

BEGIN {
    PROCINFO["sorted_in"]="@val_num_asc";
    a["line 1"]="1";
    a["line 10"]="10";
    a["line 3"]="3";
    
    for(key in a) {
        print "a["key"]="a[key];
    }

    PROCINFO["sorted_in"]="@val_num_desc";  
    b["b-line 10"]="10";
    b["b-line 100"]="100";
    b["b-line 30"]="30";
    for(key in b) {
        print "b["key"]="b[key];
    }
}

OUTPUTS:

a[line 1]=1
a[line 3]=3
a[line 10]=10
b[b-line 100]=100
b[b-line 30]=30
b[b-line 10]=10

Buscar textos

Busca el texto [DEBUG] AbstractControlNode - [ENTER] onActionFromEdit para el alias CIUDAD (columna 8). Cuenta el número de ocurrencias al final y el conteo de los ids encontrados.

index($0, "[DEBUG] AbstractControlNode - [ENTER] onActionFromEdit") !=0 && $8 ~ "CIUDAD" \
{
    f++;
    printf "[%s] %s ",FILENAME,$0;
    gsub("\"","",$0);
    gsub(",","",$0);
    if ($11 in map) {
        val = map[$11];
        map[$11] = val+1;
    } else {
        map[$11] = 1;
    }
    printf "ID_SURV_APPLIED("$11")("map[$11]")\n";
}
END {
        print "FOUND=" f;
}

getline

getline: Pasa a la siguiente línea, cambiando el valor de $0:

print $0 #imprime primer línea
getline
print $0 #imprime segunda línea

Retorno:

`getline' returns 1 if it finds a record, and 0 if the end of the
file is encountered.  If there is some error in getting a record, such
as a file that cannot be opened, then `getline' returns -1

Variables predefinidas

FNR (file number record): Número de línea actual, awk resetea FNR a cero cada que comienza el procesamiento de otro archivo (cuando se procesa más de un archivo al mismo tiempo).

NR (number record): Número de líneas que ha procesado awk hasta el momento, este no se resetea cuando se procesa más de un archivo, continúa la cuenta cuando pasa al siguiente archivo.

FILENAME: Nombre del archivo que se procesa actualmente

NF (number fields): Número de columnas que tiene la fila actual

Referencia

http://www.grymoire.com/Unix/AwkRef.html

https://www.tutorialspoint.com/awk/: Ejemplos muy claros y cortos

https://www.shortcutfoo.com/blog/awk-by-example/: Pocos ejemplos pero claros