{"id":761,"date":"2009-05-03T17:02:12","date_gmt":"2009-05-03T15:02:12","guid":{"rendered":"http:\/\/www.marblestation.com\/blog\/?p=761"},"modified":"2012-04-26T16:18:57","modified_gmt":"2012-04-26T14:18:57","slug":"tratamiento-masivo-de-datos-con-awk-alternativa-parcial-a-acl-o-sas","status":"publish","type":"post","link":"https:\/\/www.marblestation.com\/?p=761","title":{"rendered":"Tratamiento masivo de datos con AWK, alternativa parcial a ACL o SAS"},"content":{"rendered":"<p><a href=\"http:\/\/www.gnu.org\/software\/gawk\/\">GNU awk<\/a> es una herramienta muy \u00fatil para modificar archivos, buscar y transformar datos y, en general, realizar cualquier tipo de tratamiento masivo de ficheros. Con un programa awk es posible contar el n\u00famero de l\u00edneas de un archivo, seleccionar columnas, aplicar filtros, realizar cruces, borrar el \u00faltimo campo de cada l\u00ednea, hacer sumarizaciones, comprobar duplicados, muestreos, etc.<\/p>\n<p>Para aprender a utilizar AWK mediante esta peque\u00f1a gu\u00eda, lo mejor es copiar los ficheros de ejemplo que aparecen y ejecutar las instrucciones o programas que se comentan para ver directamente cual es el resultado. \u00danicamente leyendo la gu\u00eda es bastante m\u00e1s complicado entender el funcionamiento de AWK.<br \/>\n<!--more--><\/p>\n<h3>\u00cdndice de contenidos<\/h3>\n<p><a href=\"#001\">Estructura y ejecuci\u00f3n<\/a><br \/>\n<a href=\"#002\">Separadores de registros<\/a><br \/>\n<a href=\"#003\">Separadores de campos y columnas de tama\u00f1o fijo<\/a><br \/>\n<a href=\"#004\">Campos computados<\/a><br \/>\n<a href=\"#005\">Agrupaci\u00f3n de acciones<\/a><br \/>\n<a href=\"#006\">Getline<\/a><br \/>\n<a href=\"#007\">Getline y ejecuci\u00f3n de programas<\/a><br \/>\n<a href=\"#008\">Ficheros de salida<\/a><br \/>\n<a href=\"#009\">Expresiones<\/a><br \/>\n<a href=\"#010\">Conversiones de formato<\/a><br \/>\n<a href=\"#011\">Control del flujo<\/a><br \/>\n<a href=\"#012\">Arrays asociativos<\/a><br \/>\n<a href=\"#013\">Otras acciones\/funciones<\/a><br \/>\n<a href=\"#014\">Funciones<\/a><br \/>\n<a href=\"#015\">Expresiones regulares<\/a><br \/>\n<a href=\"#016\">Rendimiento \/ Profiling<\/a><br \/>\n<a href=\"#017\">Nombres de columnas<\/a><br \/>\n<a href=\"#018\">Ordenar los registros de un fichero<\/a><br \/>\n<a href=\"#019\">Sumarizaciones<\/a><br \/>\n<a href=\"#020\">Cruces de ficheros (join match + unmatch)<\/a><br \/>\n<a href=\"#021\">Muestra aleatorias<\/a><br \/>\n<a href=\"#022\">Conexiones de red<\/a><\/p>\n<h3><a name=\"001\">Estructura y ejecuci\u00f3n<\/a><\/h3>\n<p>Un programa awk presenta la siguiente estructura de reglas:<\/p>\n<pre>\r\npatr\u00f3n { acci\u00f3n }\r\npatr\u00f3n { acci\u00f3n }\r\n...\r\n<\/pre>\n<p>Los patrones corresponden a expresiones regulares (p.ej. \/foo\/) o condiciones (p.ej. $1 = &#8220;Jan&#8221;) y las acciones son funciones del lenguaje.<\/p>\n<p>En una regla puede omitirse el patr\u00f3n o la acci\u00f3n, pero no ambos. Si el patr\u00f3n se omite, entonces la acci\u00f3n se realiza para todas las l\u00edneas. Si se omite la acci\u00f3n, la acci\u00f3n por defecto es imprimir las l\u00edneas que cumplan el patr\u00f3n.<\/p>\n<p><i>Fichero_A.txt<\/i><\/p>\n<pre>\r\n\tJan 13 25 15 115\r\n\tFeb 15 32 24 226\r\n\tMar 15 24 34 228\r\n\tApr 31 52 63 420\r\n\tMay 16 34 29 208\r\n\tJun 31 42 75 492\r\n\tJul 24 34 67 436\r\n\tAug 15 34 47 316\r\n\tSep 13 55 37 277\r\n\tOct 29 54 68 525\r\n\tNov 20 87 82 577\r\n\tDec 17 35 61 401\r\n\tJan 21 36 64 620\r\n\tFeb 26 58 80 652\r\n\tMar 24 75 70 495\r\n\tApr 21 70 74 514\r\n<\/pre>\n<p>Por ejemplo, para las lineas que contienen &#8220;Jan&#8221; las imprimimos por pantalla (simulamos comando &#8216;grep&#8217;):<\/p>\n<pre>\r\n\tawk '\/Jan\/ { print $0 }' Fichero_A.txt\r\n<\/pre>\n<p>Si quisi\u00e9ramos \u00fanicamente imprimir la segunda columna utilizamos $1 (el resto las podemos encontrar en $2, $3, etc.):<\/p>\n<pre>\r\n\tawk '\/Jan\/ { print $1 }' Fichero_A.txt\r\n<\/pre>\n<p>Tambi\u00e9n podemos realizar sumarizaciones, por ejemplo, sumamos los valores de la quinta columna de las lineas que tengan en la primera columna &#8220;Jan&#8221;:<\/p>\n<pre>\r\n\tawk '$1 == \"Jan\" { sum += $5 } END { print sum }' Fichero_A.txt\r\n<\/pre>\n<p>Como se puede observar, se han definido 2 reglas:<\/p>\n<ul>\n<li>Si la primera columna es &#8220;Jan&#8221; entonces acumulamos el quinto valor en una variable.<\/li>\n<li>Si es el final del fichero (END {&#8230;}), imprimimos el valor de la variable.<\/li>\n<\/ul>\n<p>Es muy habitual ver ejemplos de AWK en una \u00fanica l\u00ednea, y justamente por ese motivo se ha ganado la fama de complejo. Pero en realidad podemos crear ficheros de texto con todas las instrucciones bien tabuladas y que permiten una mejor comprensi\u00f3n del objetivo del programa. Por ejemplo, podemos crear el fichero &#8216;test.awk&#8217;:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\t$1 == \"Jan\" { sum += $5 }\r\n\tEND { print sum }\r\n<\/pre>\n<blockquote><p><i>* Los comentarios dentro del programa awk se marcan con &#8220;#&#8221;.<\/i><\/p><\/blockquote>\n<p>Para ejecutarlo, utilizaremos la siguiente sentencia:<\/p>\n<pre>\r\n\tawk -f test.awk Fichero_A.txt\r\n<\/pre>\n<p>O si el fichero tiene permisos de ejecuci\u00f3n:<\/p>\n<pre>\r\n\tchmod 755 test.awk\r\n\t.\/test.awk Fichero_A.txt\r\n<\/pre>\n<p>El primer argumento siempre corresponde al fichero sobre el cual vamos a ejecutar el programa. De hecho, desde el propio programa podemos acceder al listado de argumentos con el que se ha llamado:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\tprint ARGC      # N\u00famero de argumentos\r\n\t\tprint ARGV[0]   # Siempre \"awk\"\r\n\t\tprint ARGV[1]   # Primer argumento\r\n\t\tprint ARGV[2]   # Segundo argumento\r\n\t}\r\n<\/pre>\n<p>Incluso, podr\u00edamos modificar los argumentos desde el propio programa para que no sea necesario especificar los ficheros por linea de comandos:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\tARGC = 2 # Uno m\u00e1s que el n\u00famero de ficheros de entrada que indiquemos.\r\n\t\tARGV[1] = \"file1\"\r\n\t}\r\n\t{ \r\n\t\tprint $0\r\n\t}\r\n<\/pre>\n<h3><a name=\"002\">Separadores de registros<\/a><\/h3>\n<p>Awk realiza un tratamiento registro a registro. Por defecto los registros se encuentran delimitados por el final de l\u00ednea \\n y por tanto, un registro = una l\u00ednea. No obstante, podemos cambiar el s\u00edmbolo que denota el final de un registro a otro car\u00e1cter:<\/p>\n<pre>\r\n    #!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t        ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" \r\n\t        RS = \"1\"\r\n\t}\r\n\t{ print $0 }\r\n<\/pre>\n<p>En el anterior programa, hemos cambiado la variable RS de forma que awk compondr\u00e1 el primer registro leyendo todos los caracteres hasta encontrar un &#8220;1&#8221; y as\u00ed sucesivamente.<\/p>\n<p>\u00bfQu\u00e9 utilidad tiene este mecanismo? Por ejemplo, en caso de que tengamos un fichero donde los registros se encuentren separados por lineas en blanco como el siguiente:<\/p>\n<p><i>Fichero_Cortado.txt<\/i><\/p>\n<pre>\r\n\tPrimer_registro dato1 dato2\r\n\tdato3 dato4\r\n\r\n\tSegudo_registro dato1 dato2\r\n\r\n\tTercer_registro dato1 dato2 dato3\r\n<\/pre>\n<p>Podemos establecer RS = &#8220;&#8221;, entonces awk compondr\u00e1 los registros desde el primer car\u00e1cter hasta que encuentra una l\u00ednea en blanco. Ve\u00e1moslo con un ejemplo:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t        ARGC = 2 ; ARGV[1] = \"Fichero_Cortado.txt\"\r\n\t        RS = \"\"\r\n\t}\r\n\t{ print FNR \" \" NR \" \" $1 \" \" $2 \" \" $3 \" \" $4 \" \" $5 } \r\n<\/pre>\n<p>El anterior programa nos reformatear\u00eda el archivo a:<\/p>\n<pre>\r\n\t1 1 Primer_registro dato1 dato2 dato3 dato4\r\n\t2 2 Segudo_registro dato1 dato2  \r\n\t3 3 Tercer_registro dato1 dato2 dato3 \r\n<\/pre>\n<p>Si os fij\u00e1is, hemos utilizado la variable FNR que indica el n\u00famero de registro del fichero actual y NR que hace referencia al n\u00famero de registro total (solo tiene sentido en caso de que hayamos utilizado varios ficheros de entrada).<\/p>\n<h3><a name=\"003\">Separadores de campos y columnas de tama\u00f1o fijo<\/a><\/h3>\n<p>Por defecto awk identifica las columnas utilizando como separador el espacio o el tabulador (si hay varios consecutivos son ignorados). Es posible modificar este separador mediante la variable FS:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { FS = \";\" }\r\n<\/pre>\n<p>Incluso podr\u00edamos utilizar expresiones regulares para indicar delimitaciones:<\/p>\n<pre>\r\n\tFS = \", \\t\" # Coma y tabulador\r\n\tFS = \" \" # Por defecto\r\n\tFS = \"[ ]\" # Un \u00fanico espacio, si hay varios se consideraran campos vac\u00edos\r\n<\/pre>\n<p>En el caso de que queramos modificar el delimitador de registros y columnas para la salida utilizaremos las variables ORS y OFS respectivamente:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t   ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\"\r\n\t   FS = \" \" # Delimitador columnas\r\n\t   OFS = \";\"\r\n\t   RS = \"\\n\" # Delimitador registros\r\n\t   ORS = \"[FIN]\\n\"\r\n\t}\t\r\n\t{ print $1, $2, $3 }\r\n<\/pre>\n<p>Para la interpretaci\u00f3n de ficheros que no disponen de delimitadores, sino que tienen columnas de tama\u00f1o fijo como por ejemplo:<\/p>\n<p><i>Fichero_ColumnasFijas.txt<\/i><\/p>\n<pre>\r\n\t937563.51176.9000.0043.9600.0801\r\n\t937663.52276.9060.0043.9620.0801\r\n\t937763.51476.9360.0043.9660.0801\r\n\t937863.53776.9240.0043.9670.0801\r\n\t937961.96778.5430.0044.1090.0801\r\n\t938061.95178.5420.0044.1080.0801\r\n<\/pre>\n<p>Podemos utilizar la variable FIELDWIDTHS, donde especificaremos en orden el tama\u00f1o de cada uno de los campos:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t    ARGC = 2 ; ARGV[1] = \"Fichero_ColumnasFijas.txt\"\r\n\t    FIELDWIDTHS = \"4 6 6 5 5 5 1\"   # Tama\u00f1o de cada campo en orden\r\n\t}\r\n\t{ print $1, $2, $3, $4, $5, $6, $7 }\r\n<\/pre>\n<p>Para hacer referencia al registro completo hemos utilizado $0, pero si queremos acceder a columnas concretas se utiliza $1, $2,&#8230; $NF (variable que hace referencia al \u00faltimo). Cabe mencionar que si la columna no existe, no se produce ning\u00fan error&#8230; simplemente se muestra una cadena vac\u00eda.<\/p>\n<p>Si bien $NF apunta a la \u00faltima columna, NF nos indica el n\u00famero de columnas que tiene el registro. Podemos utilizar ese valor para acceder a un campo anterior. El siguiente ejemplo nos mostrar\u00eda el pen\u00faltimo campo de cada registro:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ print $(NF-1) }\r\n<\/pre>\n<p>De hecho, podemos utilizar $() para indicar dentro de los par\u00e9ntesis cualquier tipo de operaci\u00f3n (p.ej. $(2*2-1))<\/p>\n<h3><a name=\"004\">Campos computados<\/a><\/h3>\n<p>Los campos, adem\u00e1s de ser mostrados, pueden ser modificados directamente o incluso creados (p.ej. campos computados):<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ $2 = $2 - 10 }\r\n\t{ $(NF+1) = \"Nuevo campo\" }\r\n\t{ print $0 }\r\n<\/pre>\n<h3><a name=\"005\">Agrupaci\u00f3n de acciones<\/a><\/h3>\n<p>No es necesario tener una regla para cada acci\u00f3n ( {acci\u00f3n} {acci\u00f3n} &#8230; ), sino que es posible agrupar diversas acciones en una \u00fanica regla:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ \r\n\t\t$2 = $2 - 10\r\n\t\t$(NF+1) = \"Nuevo campo\"\r\n\t\tprint $0\r\n\t}\r\n<\/pre>\n<p>O podemos poner todas las acciones en la misma l\u00ednea pero separadas por &#8220;;&#8221;, aunque esta opci\u00f3n complica la lectura del c\u00f3digo:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ $2 = $2 - 10 ; $(NF+1) = \"Nuevo campo\" ;  print $0 }\r\n<\/pre>\n<h3><a name=\"006\">Getline<\/a><\/h3>\n<p>Como hemos indicado, para cada registro se ejecutan todas las reglas que definimos en nuestro programa. No obstante, podemos variar ese comportamiento utilizando getline:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ \r\n\t    print \"Linea impar: \" NR \" \" $0\r\n\t    getline\r\n\t    print \"Linea par:   \" NR \" \" $0\r\n\t}\r\n<\/pre>\n<p>El anterior programa leer\u00e1 el primer registro y lo imprimir\u00e1 a\u00f1adiendo &#8220;Linea impar&#8221;. A continuaci\u00f3n, getline lee el siguiente registro y lo almacena en $0 (columnas en $1, $2&#8230; ) para imprimirlo de nuevo a\u00f1adiendo &#8220;Linea par&#8221;. Finalmente, no es necesario volver a indicar getline dado que al llegar al final de programa autom\u00e1ticamente awk salta al siguiente registro.<\/p>\n<p>Quiz\u00e1s nos interese que getline no &#8220;machaque&#8221; la informaci\u00f3n del registro actual almacenado en $0, para ello:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ \r\n\t    print \"Linea impar: \" NR \" \" $0\r\n\t    getline nueva\r\n\t    print \"Linea par:   \" NR \" \" nueva\r\n\t}\r\n<\/pre>\n<p>El nuevo registro se guardar\u00e1 en la variable &#8220;nueva&#8221; y por tanto podr\u00edamos hacer comparaciones de columnas con el anterior almacenado en $0. Por ejemplo, esto nos permitir\u00e1 invertir el orden de los registros en grupos de dos:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ \r\n\t    getline nueva\r\n\t    print \"Linea nueva:      \" nueva\r\n\t    print \"Linea anterior:   \" $0\r\n\t}\r\n<\/pre>\n<p>Tambi\u00e9n podemos utilizar getline para leer un registro de un fichero que no haya sido indicado por linea de comandos, por ejemplo:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{ \r\n\t    print $0\r\n\t    getline datos_aux < \"\/etc\/passwd\"\r\n\t}\r\n<\/pre>\n<p>Con esta caracter\u00edstica podremos realizar cruces entre ficheros como veremos m\u00e1s adelante.<\/p>\n<p>Es importante saber que el comando getline devuelve un 1 si encuentra un registro, y 0 si se encuentra el final del fichero. Por otra parte, si se produce alg\u00fan error (p.ej. no existe el fichero) entonces getline devolver\u00e1 un -1.<\/p>\n<h3><a name=\"007\">Getline y ejecuci\u00f3n de programas<\/a><\/h3>\n<p>Getline tambi\u00e9n nos permite leer la salida de comandos mediante el uso de pipes (p.ej. \"\/bin\/date\" | getline fecha):<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\tBEGIN {\r\n\t    if ((\"\/bin\/date\" | getline fecha) == 1) {\r\n\t        print \"Fichero generado: \" fecha\r\n\t    } else {\r\n\t        print \"Fichero generado en fecha desconocida\"\r\n\t    }\r\n\t    close(\"\/bin\/date\")\r\n\t}\r\n\t{\r\n\t    print $0 \r\n\t}\r\n<\/pre>\n<p>En el ejemplo anterior, validamos que getline lee correctamente un registro e imprimimos la hora. La acci\u00f3n close permite cerrar pipes o ficheros y as\u00ed, en caso de que volvamos a ejecutar el comando o a leer el fichero empezar\u00eda de nuevo desde cero.<\/p>\n<h3><a name=\"008\">Ficheros de salida<\/a><\/h3>\n<p>Hasta el momento hemos utilizado la acci\u00f3n &#8220;print&#8221; para mostrar registros por pantalla, si quisi\u00e9semos tener un mayor control del output podr\u00edamos utilizar &#8220;printf&#8221; de forma muy similar a como se realiza en el lenguaje C:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\tNR == 1 {\r\n\t    printf \"Entero %i\\n\", $1 \r\n\t    printf \"Decimal %f\\n\", $1\r\n\t    printf \"Caracter ASCII %c\\n\", $1\r\n\t    printf \"Notaci\u00f3n exponencial %e\\n\", $1\r\n\t    printf \"Cadena %s\\n\", $1\r\n\t    printf \"Octal %o\\n\", $1\r\n\t    printf \"Hexadecimal %X\\n\", $1\r\n\t    # Modificadores \r\n\t    printf \"Cadena a la derecha en columna de 10: %10i\\n\", $1\r\n\t    printf \"Cadena a la izquierda en columna de 10: %-10i\\n\", $1\r\n\t    printf \"N\u00famero con 2 decimales %.2f\\n\", $1\r\n\t}\r\n<\/pre>\n<p>Como hemos visto, tanto print como printf escriben por defecto a la salida est\u00e1ndar. Quiz\u00e1s nos interese hacer que la salida se escriba en un fichero o se redirija a un comando mediante una pipe:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN { ARGC = 2 ; ARGV[1] = \"Fichero_A.txt\" }\r\n\t{\r\n\t    print $0 > \"001_fichero_salida.txt\" # Fichero nuevo (borra si ya existia)\r\n\t    print $0 >> \"001_fichero_acumulativo.txt\" # Append si el fichero existe\r\n\t    print $0 | \"\/bin\/cat\" # Pipe a un comando\r\n\t    print $0 | \"sort -r > 001_fichero_salida.ordenado.txt\"\r\n\t}\r\n\tEND { close(\"\/bin\/cat\") ; close(\"sort -r > 001_fichero_salida.ordenado.txt\") }\r\n<\/pre>\n<p>Como se observa en el ejemplo anterior, podemos aprovechar las ventajas de las pipes para combinar la potencia de awk con otros comandos como &#8220;sort&#8221;. Hay que tener en cuenta que el comando no se ejecuta hasta que la pipe no es cerrada con close o por el fin del programa, esto implica que toda la salida se guarda en memoria mientras tanto.<\/p>\n<h3><a name=\"009\">Expresiones<\/a><\/h3>\n<p>Operadores aritm\u00e9ticos<\/p>\n<p>\tSuma: x+y<br \/>\n\tResta: x-y<br \/>\n\tNegaci\u00f3n: -x<br \/>\n\tMultiplicaci\u00f3n: x*y<br \/>\n\tDivisi\u00f3n: x\/y<br \/>\n\tResto: x%y<br \/>\n\tExponente: x**y<\/p>\n<p>Comparaciones: x&#60;y , x&#038;#60=y, x>y, x>=y, x==y, x!=y, x~y (&#8216;y&#8217; debe ser una expresi\u00f3n regular, p.ej. \/^test\/)<\/p>\n<p>Expresiones booleanas: &#038;&#038;, ||, ! (negaci\u00f3n)<\/p>\n<h3><a name=\"010\">Conversiones de formato<\/a><\/h3>\n<p>Las conversiones entre n\u00fameros y cadenas son autom\u00e1ticas (en caso de que una cadena no pueda ser convertida se traduce como 0):<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t    edad = \"27\"\r\n\t    edad = edad + 1.5\r\n\t    print \"Tengo \" edad \" a\u00f1os.\"\r\n\t}\r\n<\/pre>\n<p>En el ejemplo anterior edad es inicializada con una cadena, a continuaci\u00f3n se utiliza en una suma y se convierte autom\u00e1ticamente a num\u00e9rico. Finalment print convierte el n\u00famero a cadena para permitir la concatenaci\u00f3n.<\/p>\n<h3><a name=\"011\">Control del flujo<\/a><\/h3>\n<p>Las estructuras de control de awk son muy parecidas a las del lenguaje C:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\t# Condicional\r\n\t\tedad = \"27\"\r\n\t\tif (edad == 27) {\r\n\t\t\tprint \"Si\"\r\n\t\t} else {\r\n\t\t\tprint \"No\"\r\n\t\t}\r\n\r\n\t\t# Bucles\r\n\t\ti = 1\r\n\t\tprint \"While\"\r\n\t\twhile (i < = 3) {\r\n\t\t\tprint i\r\n\t\t\ti++\r\n\t\t}\r\n\r\n\t\tprint \"Do while\"\r\n\t\tdo {\r\n\t\t\ti--\r\n\t\t\tprint i\r\n\t\t} while (i > 1)\r\n\r\n\t\tprint \"Bucle for\"\r\n\t\tfor(i=1; i< = 3; i++) {\r\n\t\t\tprint i\r\n\t\t}\r\n\t}\r\n<\/pre>\n<p>En los bucles podemos utilizar \"continue\" para saltar a la siguiente iteraci\u00f3n o \"break\" para finalizar. A nivel de programa, como awk ejecuta todas las reglas para cada registro de los ficheros de entrada, en cierta forma se trata de un bucle que podemos hacer saltar al siguiente registro con \"next\" o parar con \"exit\".<\/p>\n<h3><a name=\"012\">Arrays asociativos<\/a><\/h3>\n<p>Como estructura de almacenamiento de informaci\u00f3n ya hemos trabajado con variables, pero awk nos ofrece la posibilidad de usar arrays asociativos (diccionarios):<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\tv[0] = \"Elemento\"\r\n\t\tv[1] = \"Cadena\"\r\n\t\tv[\"Clave\"] = \"Valor\"\r\n\t\tv[\"key\"] = 25\r\n\r\n\t\t# Comprobar la existencia de una clave\r\n\t\tif (\"Clave\" in v) {\r\n\t\t\tprint \"Clave encontrada!\"\r\n\t\t}\r\n\r\n\t\t# Borrado de elementos\r\n\t\tdelete v[\"Clave\"]\r\n\r\n\t\t# Recorrer un array\r\n\t\tfor(key in v) {\r\n\t\t\tprint key \" -> \" v[key]\r\n\t\t}\r\n\r\n\t\t# Arrays multidimensionales\r\n\t\tw[0, 1] = \";-)\"\r\n\t}\r\n<\/pre>\n<h3><a name=\"013\">Otras acciones\/funciones<\/a><\/h3>\n<p>Veamos otras acciones \u00fatiles de awk:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\t## N\u00fameros\r\n\t\tsrand() # Inicializa semilla con la fecha\/hora actual\r\n\t\tprint rand() # Decimal aleatorio entre 0 y 1\r\n\t\tprint int(2.45) # Parte entera\r\n\t\tnum = sprintf(\"%.2f\", 2.256821)\t# Redondeo a 2 decimales\r\n\t\tprint num\r\n\t\tprint sqrt(4) # Raiz cuadrada\r\n\t\tprint \"Exponencial \" exp(1) \" sinus \" sin(2) \" cosinus \" cos(2) \" logaritmo \" log(2)\r\n\r\n\t\t## Strings\r\n\t\tprint index(\"cadena\", \"de\") # Posici\u00f3n donde se encuentra \"de\" (0 si no lo encuentra)\r\n\t\tprint length(\"cadena\") # Tama\u00f1o\r\n\t\tprint match(\"cadena\", \/$.*\/) # Compara con expresi\u00f3n regular\r\n\t\tstr = \"1;2;3\"\r\n\t\tsplit(str, v, \";\")  # Divide y guarda en un vector\/array\r\n\t\tprint v[1] \" \" v[2] \" \" v[3]\r\n\t\tsub(\/;\/, \"-\", str) # Substituye la primera aparici\u00f3n \";\" por \"-\"\r\n\t\tprint str\r\n\t\tgsub(\/;\/, \"-\", str) # Substituye todos los \";\" por \"-\"\r\n\t\tprint str\r\n\t\tprint substr(str, 1, 3) # Desde la posici\u00f3n 1, devuelve los siguientes 3 caracteres\r\n\t\tprint tolower(\"MAYUSCULAS\")\r\n\t\tprint toupper(\"minusculas\")\r\n\r\n\t\t#Fechas y horas\r\n\t\tt1 = systime()  # Fecha\/hora actual\r\n\t\tt2 = mktime(\"2009 01 01 00 00 00\") # YYYY MM DD HH MM SS \r\n\t\tprint \"Tiempo transcurrido desde 01-01-2009: \" \r\n\t\tprint \"- Segundos: \" t1 - t2\r\n\t\tprint \"- Minutos: \" (t1 - t2)\/60\r\n\t\tprint \"- Horas: \" (t1 - t2)\/60\/60\r\n\t\tprint \"- Dias: \" (t1 - t2)\/60\/60\/24\r\n\r\n\t\t# Formatear hora\/fecha actual\r\n\t\tprint strftime(\"%Y-%m-%d %H:%M:%S\", systime()) # 2009-02-14 17:46:28\r\n\t}\r\n<\/pre>\n<p>Para el formateo de fechas se utiliza la siguiente nomenclatura:<\/p>\n<pre>\r\n\t%a\t%A\tNombre del dia (p.ej. Lunes)\r\n\t%b\t%B\tNombre del mes\r\n\t%d\t\tD\u00eda del mes\r\n\t%H\t\tHora (24)\r\n\t%I\t\tHora (12)\r\n\t%j\t\tD\u00eda del a\u00f1o (001-365)\r\n\t%m\t\tMes\r\n\t%M\t\tMinuto\r\n\t%p\t\tAM\/PM\r\n\t%S\t\tSegundo\r\n\t%u\t\tN\u00famero del d\u00eda de la semana (lunes = 1)\r\n\t%U\t\tN\u00famero de la semana del a\u00f1o\r\n\t%Y\t\tA\u00f1o\r\n<\/pre>\n<h3><a name=\"014\">Funciones<\/a><\/h3>\n<p>Awk nos permite definir nuestras propias funciones:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tfunction p (str) {\r\n\t\tprint str\r\n\t\treturn 0    # Opcional\r\n\t}\r\n\r\n\tBEGIN {\r\n\t\tp(\"Hola mundo!\")\r\n\t}\r\n<\/pre>\n<p>Esto nos permitir\u00eda hacer funciones gen\u00e9ricas que nos ahorren c\u00f3digo repetitivo.<\/p>\n<h3><a name=\"015\">Expresiones regulares<\/a><\/h3>\n<pre>\r\n^\tInicio de la cadena\r\n$\tFinal de la cadena\r\n.\tCualquier car\u00e1cter \u00fanico (excepto newline)\r\n[]\tConjunto de caracteres\r\n\t[MVX]\tEncaja con M, V o X\r\n\t[0-9]\tEncaja con un d\u00edgito\r\n[^]\tConjunto de car\u00e1cter complementario\r\n\t[^0-9]\tEncaja con cualquier car\u00e1cter excepto un d\u00edgito\r\n|\tAlternativas\r\n\tA|[0-9]\tEncaja con \"A\" o un d\u00edgito\r\n()\tSe pueden utilizar par\u00e9ntesis para agrupar expresiones\r\n\t(A|[0-9])Z\tEncaja con \"A\" o d\u00edgito, que uno u otro vaya seguido de Z\r\n*\tLa expresi\u00f3n precedente se puede repetir 0 o m\u00e1s veces\r\n\tA*\tEncaja con nada o un n\u00famero indefinido de A\r\n+\tLa expresi\u00f3n precedente tendr\u00e1 lugar 1 o m\u00e1s veces\r\n\tA+\tEncaja con una o m\u00e1s A\r\n?\tLa expresi\u00f3n precedente no aparecer\u00e1 o aparecer\u00e1 1 \u00fanica vez\r\n<\/pre>\n<p>Para que awk no sea sensible a las may\u00fasculas\/min\u00fasculas podemos jugar con las acciones tolower\/toupper o bien establecer la variable IGNORECASE a 1.<\/p>\n<h3><a name=\"016\">Rendimiento \/ Profiling<\/a><\/h3>\n<p>Podemos ejecutar nuestro programa con pgawk para que genere un fichero denominado &#8220;awkprof.out&#8221; donde para cada linea se indica el n\u00famero de veces que ha sido ejecutada. As\u00ed podremos identificar aspectos a optimizar.<\/p>\n<h3><a name=\"017\">Nombres de columnas<\/a><\/h3>\n<p>Por defecto, awk nos permite acceder a las columnas mediante el uso de $1, $2, etc&#8230; pero esto puede ser un inconveniente si cambia el n\u00famero o orden de las columnas de nuestros ficheros, dado que implicar\u00e1 un cambio forzado en las referencias que usamos en nuestro programa. Para solucionar ese problema, si disponemos de ficheros donde la primera linea es la cabecera y se anotan los nombres de las columnas, podemos crear un array que despu\u00e9s nos permita acceder como sigue:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\tARGC = 1+1 # Uno m\u00e1s que el n\u00famero de ficheros de entrada que indiquemos.\r\n\t\tARGV[1] = \"Fichero_Column.txt\"\r\n\t}\r\n\t# Cabecera\r\n\tNR==1 {\r\n\t\t\t# Guardamos nombres de columnas \r\n\t\t\tfor(i = 1; i< = NF; i++) {\r\n\t\t\t\tc[tolower($i)] = i\r\n\t\t\t}\r\n\t\t }\r\n\r\n\t# Podemos acceder a las columnas por $1, $2... o por $c[\"nombre_columna\"]\r\n\tNR > 1 { print $c[\"mes\"] }\r\n<\/pre>\n<p>De esta forma podremos acceder a las columnas usando $c[&#8220;nombre_columna&#8221;], independientemente que en el futuro la cambiemos de lugar en el fichero de entrada.<\/p>\n<h3><a name=\"018\">Ordenar los registros de un fichero<\/a><\/h3>\n<p>Utilizaremos una funci\u00f3n para ordenar array de menor a mayor, tenemos dos opciones:<\/p>\n<ol>\n<li>seg\u00fan los elementos: asort(array)<\/li>\n<li>seg\u00fan los indices: asorti(array)<\/li>\n<\/ol>\n<p>Ambas funciones &#8220;machacan&#8221; los \u00edndices originales por \u00edndices num\u00e9ricos (1, 2, 3&#8230;), para evitar perder el array original podemos usar dos argumentos: asort(array_origen, array_destino)<\/p>\n<p>Por ejemplo, para ordenar un fichero por dos campos determinados utilizando asorti:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\t{ \r\n\t\t# Queremos ordenar por los campos $2 y $1 y guardamos la linea original correspondiente\r\n\t\toriginal[$2,$1] = $0\r\n\t}\r\n\r\n\tEND {\r\n\t\t\t# En \"ordenado\" tendremos del 1 a n las claves $2,$1 ordenadas de menor a mayor\r\n\t\t\tn = asorti(original, ordenado)\r\n\t\t\tfor (i = 1; i < = n; i++) {\r\n\t\t\t\t# Imprimimos las lineas originales pero siguiendo el nuevo orden\r\n\t\t\t\tprint original[ordenado[i]]\r\n\t\t\t}\r\n\t}\r\n<\/pre>\n<h3><a name=\"019\">Sumarizaciones<\/a><\/h3>\n<p>Para realizar una sumarizaci\u00f3n de un fichero por el campo $1 y sumando los valores del campo $3, podemos utilizar el siguiente c\u00f3digo (no requiere que el fichero este ordenado):<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\t{\r\n\t\t# Si no es la primera vez que tratamos esta clave...\r\n\t\tif (count[$1] != \"\") {\r\n\t\t\tcount[$1]++\r\n\t\t\tcol_sum[$1] += $3\r\n\t\t} else {\r\n\t\t\tcount[$1] = 1\r\n\t\t\tcol_sum[$1] = $3\r\n\t\t}\r\n\t }\r\n\r\n\tEND {\r\n\t\tfor (k in count) {\r\n\t\t\tprint k \" \" count[k] \" \" col_sum[k]\r\n\t\t}\r\n\t}\r\n<\/pre>\n<p>Podr\u00edamos a\u00f1adir m\u00e1s arrays para hacer m\u00e1s sumarizaciones de columnas, o utilizar como \u00edndice m\u00e1s campos (p.ej. count[$1, $2]) si queremos utilizar varias columnas como campos clave.<\/p>\n<p>Por supuesto, con este mismo ejemplo podr\u00edamos validar si existen duplicados dado que en &#8220;count&#8221; estamos guardando el n\u00famero de apariciones.<\/p>\n<h3><a name=\"020\">Cruces de ficheros (join match + unmatch)<\/a><\/h3>\n<p>Veamos como podemos hacer el cruce de los siguientes ficheros utilizando el primer campo como clave primaria:<\/p>\n<p><i>Fichero_A.sorted.txt<\/i><\/p>\n<pre>\r\n\tApr 21 70 74 514\r\n\tApr 31 52 63 420\r\n\tAug 15 34 47 316\r\n\tFeb 15 32 24 226\r\n\tFeb 26 58 80 652\r\n\tJan 13 25 15 115\r\n\tJan 21 36 64 620\r\n\tJul 24 34 67 436\r\n\tJun 31 42 75 492\r\n\tMar 15 24 34 228\r\n\tMar 24 75 70 495\r\n\tMay 16 34 29 208\r\n\tNov 20 87 82 577\r\n\tOct 29 54 68 525\r\n\tSep 13 55 37 277\r\n<\/pre>\n<p><i>Fichero_B.sorted.txt<\/i><\/p>\n<pre>\r\n\tApr Abril\r\n\tAug Agosto\r\n\tDec Diciembre\r\n\tFeb Febrero\r\n\tJul Julio\r\n\tJun Junio\r\n\tMar Marzo\r\n\tMay Mayo\r\n\tNov Noviembre\r\n\tOct Octubre\r\n<\/pre>\n<p>Es importante observar que ambos ficheros se encuentran ordenados por la clave primaria (que ser\u00e1 la que utilizaremos para cruzar) y el Fichero_B no contiene duplicados (que ser\u00e1 el que utilizaremos como fichero secundario en el cruce).<\/p>\n<p>A continuaci\u00f3n el c\u00f3digo del cruce:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\t## Ficheros de entrada\r\n\t\t#  - Ordenados por las claves que van a ser utilizadas\r\n\t\t#  - El fichero secundario no puede tener duplicados\r\n\t\tprimary_file = \"Fichero_A.sort.txt\"\r\n\t\tsecondary_file = \"Fichero_B.sort.txt\"\r\n\r\n\t\t# Ficheros de salida\r\n\t\tmatch_file = \"000_A_and_B.txt\"\r\n\t\tprimary_unmatch_file = \"000_A_and_notB.txt\"\r\n\t\tsecondary_unmatch_file = \"000_notA_and_B.txt\"\r\n\r\n\t\tARGC = 1+1 # Uno m\u00e1s que el n\u00famero de ficheros de entrada que indiquemos.\r\n\t\tARGV[1] = primary_file\r\n\t}\r\n\r\n\t{\r\n\t\t# Clave primaria del fichero primario\r\n\t\tprimary_pkey = $1\r\n\r\n\t\t# Si el campo clave del fichero primario es m\u00e1s grande que el del secundario, avanzamos el registro del secundario\r\n\t\tif (reg == \"\" || primary_pkey > secondary_pkey) {\r\n\t\t\tstatus = getline reg < secondary_file\r\n\t\t\tif (status == 1) {\r\n\t\t\t\tsplit(reg, r, \" \") # Guardamos los campos en el array r[1..n]\r\n\r\n\t\t\t\t # Clave primaria del fichero secundario\r\n\t\t\t\tsecondary_pkey = r[1]\r\n\t\t\t} \r\n\t\t}\r\n\r\n\t\t# Si no se ha llegado al final del fichero del secundario\r\n\t\tif (status == 1) {\r\n\t\t\t# Cruzan\r\n\t\t\tif (primary_pkey == secondary_pkey) {\r\n\t\t\t\tprint $0, r[2] > match_file\r\n\t\t\t\tvmatch[primary_pkey] = 1 # Control para evitar detectar como no cruzado en el futuro\r\n\t\t\t}\r\n\r\n\t\t\t# Registro del primario no existe en el secundario\r\n\t\t\tif (primary_pkey < secondary_pkey &#038;&#038; !vmatch[primary_pkey]) {\r\n\t\t\t\tprint $0 > primary_unmatch_file\r\n\t\t\t}\r\n\r\n\t\t\t# Registro del secundario no existe en el primario\r\n\t\t\tif (primary_pkey > secondary_pkey  && !vmatch[secondary_pkey]) {\r\n\t\t\t\tprint reg > secondary_unmatch_file\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t# Se ha acabado el fichero secundario, por tanto todo lo pendiente del primario no existe en el secundario\r\n\t\t\tprint $0 > primary_unmatch_file\r\n\t\t}\r\n\r\n\t}\r\n<\/pre>\n<p>Como podemos observar, el c\u00f3digo necesario para llevar a cabo el cruce es algo m\u00e1s extenso que los ejemplos que hemos visto hasta ahora. No obstante, dado que awk nos da una mayor granularidad que otras herramientas comerciales de tratamiento masivo como ACL o SAS, tenemos como ventaja una mayor eficiencia y un incremento en la flexibilidad y potencia.<\/p>\n<p>El programa anterior nos genera tres ficheros de salida:<\/p>\n<ol>\n<li>&#8220;000_A_and_B.txt&#8221;: Registros que han cruzado<\/li>\n<li>&#8220;000_A_and_notB.txt&#8221;: Registros del fichero primario que no se encuentran en el secundario<\/li>\n<li>&#8220;000_notA_and_B.txt&#8221;: Registros del fichero secundario que no se encuentran en el primario<\/li>\n<\/ol>\n<p>Tal y hemos visto, los fichero de entrada deben estar ordenados para que el cruce funcione correctamente. A continuaci\u00f3n tenemos otro programa que realiza la misma funcionalidad y que no requiere que los ficheros est\u00e9n ordenados. Para ello el fichero secundario ser\u00e1 cargado completamente en memoria antes de iniciar el cruce, esto implica un mayor rendimiento en cuanto al tiempo de ejecuci\u00f3n pero un consumo muy superior de memoria. Si tenemos suficiente memoria RAM o los ficheros con los que trabajamos no son excesivamente grandes, vale la pena considerar este algoritmo:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\t## Ficheros de entrada\r\n\t\t#  - No es necesario que esten ordenados\r\n\t\t#  - El fichero secundario no puede tener duplicados\r\n\t\tprimary_file = \"Fichero_A.sort.txt\"\r\n\t\tsecondary_file = \"Fichero_B.sort.txt\"\r\n\r\n\t\t# Ficheros de salida\r\n\t\tmatch_file = \"000_A_and_B.txt\"\r\n\t\tprimary_unmatch_file = \"000_A_and_notB.txt\"\r\n\t\tsecondary_unmatch_file = \"000_notA_and_B.txt\"\r\n\r\n\t\tARGC = 1+1 # Uno m\u00e1s que el n\u00famero de ficheros de entrada que indiquemos.\r\n\t\tARGV[1] = primary_file\r\n\r\n\t\t# Cargamos el fichero secundario en memoria en el array sec\r\n\t\twhile ((getline < secondary_file) > 0) {\r\n\t\t\tsec[$1] = $0\r\n\t\t\tsec_matched[$1] = 0 # Array que ser\u00e1 utilizado para identificar los reg. del secundario que no cruzan\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t{\r\n\t\t# Registros que cruzan\r\n\t\tif (sec[$1]) {\r\n\t\t\tsec_matched[$1]++ # Marcamos registro secundario como cruzado\r\n\r\n\t\t\t# A\u00f1adimos el camo 2 del registro secundario a toda la linea del primario\r\n\t\t\tsplit(sec[$1], s, \" \")\r\n\t\t\tprint $0, s[2] > match_file\r\n\t\t} else {\r\n\t\t\t# Registros del primario que no existen en el secundario\r\n\t\t\tprint $0 > primary_unmatch_file\r\n\t\t}\r\n\t}\r\n\r\n\tEND {\r\n\t\t# Registros del secundario que no existen en el primario\r\n\t\tfor(k in sec_matched) {\r\n\t\t\tif (sec_matched[k] == 0) {\r\n\t\t\t\tprint sec[k] > secondary_unmatch_file\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n<\/pre>\n<h3><a name=\"021\">Muestra aleatorias<\/a><\/h3>\n<p>Para la obtenci\u00f3n de N registros aleatorios de un fichero, podemos utilizar la siguiente implementaci\u00f3n del algoritmo R de Waterman, mediante el cual no es necesario conocer de antemano el n\u00famero total de registros y por tanto solo tendremos que leer una vez el fichero:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\t# Waterman's Algorithm R for random sampling\r\n\t# by way of Knuth's The Art of Computer Programming, volume 2\r\n\r\n\tBEGIN {\r\n\t\tARGC = 1+1 # Uno m\u00e1s que el n\u00famero de ficheros de entrada que indiquemos.\r\n\t\tARGV[1] = \"Fichero_A.txt\"\r\n\r\n\t\tn = 2 # Tama\u00f1o la muestra\r\n\t\tt = n\r\n\t\tsrand() # Inicializamos semilla\r\n\t}\r\n\r\n\t# Si el numero de registros tratados es inferior al tama\u00f1o de la muestra\r\n\tNR < = n {\r\n\t\t# Construimos el pool inicial de muestras con los primeros registros consecutivos\r\n\t\tpool[NR] = $0\r\n\t\tplaces[NR] = NR\r\n\t\tnext\r\n\t}\r\n\r\n\t# Si el numero de registros tratados es superior al tama\u00f1o de la muestra\r\n\tNR > n {\r\n\t\tt++ # Decrementamos las probabilidades de que el siguiente numero aleatorio sea inferior al numero de muestras\r\n\t\tM = int(rand()*t) + 1\r\n\t\tif (M < = n) {\r\n\t\t\t# Substituimos registro del pool\r\n\t\t\trec = places[M]         # Borramos la muestra previamente seleccionada\r\n\t\t\tdelete pool[rec]\r\n\t\t\tpool[NR] = $0           # A\u00f1adimos la nueva muestra\r\n\t\t\tplaces[M] = NR\r\n\t\t}\r\n\t}\r\n\r\n\tEND {\r\n\t\tif (NR < n) {\r\n\t\t\tprint \"El numero de registros es inferior al muestreo definido\" > \"\/dev\/stderr\"\r\n\t\t\texit\r\n\t\t}\r\n\r\n\t\t# Dado que asorti es una funcion que ordena alfab\u00e9ticamente y no seg\u00fan el numero real:\r\n\t\t# Convertimos claves numericas del array pool a cadenas con ceros (p.ej. 1 -> 01)\r\n\t\tpad = length(NR)\r\n\t\tfor (i in pool) {\r\n\t\t\tnew_index = sprintf(\"%0\" pad \"i\", i)\r\n\t\t\tnewpool[new_index] = pool[i]\r\n\t\t}\r\n\t\t# Visualizamos la muestra en orden\r\n\t\tx = asorti(newpool, ordered)\r\n\t\tfor (i = 1; i < = x; i++)\r\n\t\t\tprint newpool[ordered[i]]\r\n\t}\r\n<\/pre>\n<h3><a name=\"022\">Conexiones de red<\/a><\/h3>\n<p>Awk tambi\u00e9n nos permite realizar conexiones de red mediante el uso de la siguiente ruta:<\/p>\n<pre>\r\n\t\"\/inet\/protocol\/local-port\/remote-host\/remote-port\"\r\n<\/pre>\n<p>El protocolo puede ser &#8216;tcp\u2019, \u2018udp\u2019, or \u2018raw\u2019 y como puerto local podemos dejarlo a 0 para que el sistema lo establezca por nosotros.<\/p>\n<p>Veamos un ejemplo de conexi\u00f3n a Yahoo Finance para obtener la cotizaci\u00f3n del \u00edndice IBEX35 y el cambio Euro-Dolar:<\/p>\n<pre>\r\n\t#!\/usr\/bin\/awk -f\r\n\tBEGIN {\r\n\t\tFS = \",\"        # Separador de campos\r\n\r\n\t\tNetService = \"\/inet\/tcp\/0\/download.finance.yahoo.com\/80\"\r\n\t\tprint \"GET http:\/\/download.finance.yahoo.com\/d\/quotes.csv?f=snl1d1t1c4&e=.csv&s=^IBEX+EURUSD=X\" |& NetService\r\n\t\tprint \"symbol,name,last,date,time,currency\"\r\n\t\twhile ((NetService |& getline) > 0) {\r\n\t\t\tgsub(\/\"\/, \"\", $0)       # Quitamos las \" de los strings\r\n\t\t\tprint $0\r\n\t\t}\r\n\t\tclose(NetService)\r\n\t}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>GNU awk es una herramienta muy \u00fatil para modificar archivos, buscar y transformar datos y, en general, realizar cualquier tipo de tratamiento masivo de ficheros. Con un programa awk es posible contar el n\u00famero de l\u00edneas de un archivo, seleccionar columnas, aplicar filtros, realizar cruces, borrar el \u00faltimo campo de cada l\u00ednea, hacer sumarizaciones, comprobar &hellip; <a href=\"https:\/\/www.marblestation.com\/?p=761\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Tratamiento masivo de datos con AWK, alternativa parcial a ACL o SAS<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,6],"tags":[],"class_list":["post-761","post","type-post","status-publish","format-standard","hentry","category-espanyol","category-tecnologia"],"_links":{"self":[{"href":"https:\/\/www.marblestation.com\/index.php?rest_route=\/wp\/v2\/posts\/761","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.marblestation.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.marblestation.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.marblestation.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.marblestation.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=761"}],"version-history":[{"count":8,"href":"https:\/\/www.marblestation.com\/index.php?rest_route=\/wp\/v2\/posts\/761\/revisions"}],"predecessor-version":[{"id":1223,"href":"https:\/\/www.marblestation.com\/index.php?rest_route=\/wp\/v2\/posts\/761\/revisions\/1223"}],"wp:attachment":[{"href":"https:\/\/www.marblestation.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=761"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.marblestation.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=761"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.marblestation.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=761"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}