AWK
AWK (awk) es un lenguaje específico de dominio diseñado para el procesamiento de texto y normalmente se usa como una herramienta de extracción de datos y generación de informes. Al igual que sed y grep, es un filtro y es una característica estándar de la mayoría de los sistemas operativos similares a Unix.
El lenguaje AWK es un lenguaje de secuencias de comandos basado en datos que consiste en un conjunto de acciones que se deben realizar contra flujos de datos textuales, ya sea que se ejecuten directamente en archivos o se utilicen como parte de una canalización, con el fin de extraer o transformar texto, como como producir informes formateados. El lenguaje utiliza ampliamente el tipo de datos de cadena, matrices asociativas (es decir, matrices indexadas por cadenas clave) y expresiones regulares. Si bien AWK tiene un dominio de aplicación previsto limitado y fue diseñado especialmente para admitir programas de una sola línea, el lenguaje es completo de Turing, e incluso los primeros usuarios de AWK de Bell Labs a menudo escribieron grandes programas AWK bien estructurados.
AWK se creó en Bell Labs en la década de 1970 y su nombre se deriva de los apellidos de sus autores: Alfred Aho, Peter Weinberger y Brian Kernighan. El acrónimo se pronuncia igual que el pájaro auk, que se encuentra en la portada de El lenguaje de programación AWK. Cuando se escribe en minúsculas, como awk
, se refiere al programa Unix o Plan 9 que ejecuta scripts escritos en el lenguaje de programación AWK.
Historia
AWK fue desarrollado inicialmente en 1977 por Alfred Aho (autor de egrep), Peter J. Weinberger (quien trabajó en pequeñas bases de datos relacionales) y Brian Kernighan. AWK toma su nombre de sus respectivas iniciales. Según Kernighan, uno de los objetivos de AWK era tener una herramienta que manipulara fácilmente tanto números como cadenas. AWK también se inspiró en el lenguaje de programación de Marc Rochkind que se utilizó para buscar patrones en los datos de entrada y se implementó mediante yacc.
Como una de las primeras herramientas que aparecieron en la versión 7 de Unix, AWK agregó características computacionales a un canal de Unix además del shell Bourne, el único lenguaje de secuencias de comandos disponible en un entorno estándar de Unix. Es una de las utilidades obligatorias de la especificación UNIX única y es requerida por la especificación base estándar de Linux.
AWK se revisó y amplió significativamente entre 1985 y 1988, lo que resultó en la implementación de GNU AWK escrita por Paul Rubin, Jay Fenlason y Richard Stallman, lanzada en 1988. GNU AWK puede ser la versión más implementada porque se incluye con Paquetes de Linux basados en GNU. GNU AWK ha sido mantenido únicamente por Arnold Robbins desde 1994. La fuente nawk (Nueva AWK) de Brian Kernighan se lanzó por primera vez en 1993 sin publicidad y públicamente desde finales de la década de 1990; muchos sistemas BSD lo usan para evitar la licencia GPL.
AWK fue precedido por sed (1974). Ambos fueron diseñados para el procesamiento de texto. Comparten el paradigma basado en datos y orientado a la línea, y son particularmente adecuados para escribir programas de una sola línea, debido al bucle principal implícito y las variables de línea actuales. El poder y la concisión de los primeros programas AWK, en particular el poderoso manejo de expresiones regulares y la concisión debido a las variables implícitas, que facilitan las frases ingeniosas, junto con las limitaciones de AWK en ese momento, fueron inspiraciones importantes para el lenguaje Perl (1987). En la década de 1990, Perl se hizo muy popular, compitiendo con AWK en el nicho de los lenguajes de procesamiento de texto Unix.
Estructura de los programas AWK
AWK lee la entrada una línea a la vez. Una línea se escanea para cada patrón en el programa, y para cada patrón que coincide, la acción asociada se ejecuta.
—Alfred V. Aho
Un programa AWK es una serie de pares de acciones de patrón, escrito como:
condición {} acción }condición {} acción }...
donde condición suele ser una expresión y acción es una serie de comandos. La entrada se divide en registros, donde por defecto los registros están separados por caracteres de nueva línea para que la entrada se divida en líneas. El programa prueba cada registro contra cada una de las condiciones y ejecuta la acción para cada expresión que sea verdadera. Se puede omitir la condición o la acción. La condición por defecto es hacer coincidir todos los registros. La acción predeterminada es imprimir el registro. Esta es la misma estructura de patrón-acción que sed.
Además de una expresión AWK simple, como foo == 1
o /^foo/
, la condición puede ser BEGIN
o END
que hace que la acción se ejecute antes o después de que se hayan leído todos los registros, o patrón1, patrón2 que coincide con el rango de registros que comienzan con un registro que coincide con patrón1 hasta e incluyendo el registro que coincide con patrón2 antes de volver a intentar hacer coincidir con patrón1 en las líneas subsiguientes.
Además de los operadores aritméticos y lógicos normales, las expresiones AWK incluyen el operador de tilde, ~
, que compara una expresión regular con una cadena. Como azúcar sintáctico útil, /regexp/ sin usar el operador de tilde coincide con el registro actual; esta sintaxis se deriva de sed, que a su vez la heredó del editor ed, donde /
se usa para buscar. Perl y ECMAScript adoptaron posteriormente esta sintaxis de usar barras inclinadas como delimitadores de expresiones regulares, y ahora es común. Perl también adoptó el operador tilde.
Comandos
Los comandos AWK son las declaraciones que se sustituyen por acción en los ejemplos anteriores. Los comandos AWK pueden incluir llamadas a funciones, asignaciones de variables, cálculos o cualquier combinación de los mismos. AWK contiene soporte incorporado para muchas funciones; muchos más son proporcionados por los diversos sabores de AWK. Además, algunos sabores admiten la inclusión de bibliotecas vinculadas dinámicamente, que también pueden proporcionar más funciones.
El comando de impresión
El comando imprimir se utiliza para generar texto. El texto de salida siempre termina con una cadena predefinida llamada separador de registro de salida (ORS) cuyo valor predeterminado es una nueva línea. La forma más simple de este comando es:
print
- Esto muestra el contenido del registro actual. En AWK, los registros se dividen en campos, y estos se pueden mostrar por separado:
print $1
- Muestra el primer campo del registro actual
print $1, $3
- Muestra los campos primero y tercero del registro actual, separados por una cadena predefinida llamada separador de campo de salida (OFS) cuyo valor predeterminado es un solo personaje espacial
Aunque estos campos ($X) pueden parecerse a variables (el símbolo $ indica variables en Perl), en realidad se refieren a los campos del registro actual. Un caso especial, $0, se refiere a todo el registro. De hecho, los comandos "print
" y "imprimir $0
" son idénticos en funcionalidad.
El comando imprimir también puede mostrar los resultados de cálculos y/o llamadas a funciones:
/regex_pattern/ {} # Acciones para realizar en el evento el registro (línea) coincide con el anterior regex_pattern impresión 3+2 impresión foobar()3) impresión foobar()variable) impresión pecado()3-2)}
La salida puede enviarse a un archivo:
/regex_pattern/ {} # Acciones para realizar en el evento el registro (línea) coincide con el anterior regex_pattern impresión "expresión" ■ "Nombre del fichero"}
o a través de una tubería:
/regex_pattern/ {} # Acciones para realizar en el evento el registro (línea) coincide con el anterior regex_pattern impresión "expresión" Silencio "comandante"}
Variables incorporadas
Las variables integradas de Awk incluyen las variables de campo: $1, $2, $3, etc. ($0 representa el registro completo). Contienen el texto o los valores en los campos de texto individuales de un registro.
Otras variables incluyen:
NR
: Número de registros. Mantiene un recuento actual del número de registros de entrada leídos tan lejos de todos los archivos de datos. Empieza a cero, pero nunca se reasienta automáticamente a cero.FNR
: Número de archivo de registros. Mantiene un recuento actual del número de registros de entrada leídos hasta ahora en el archivo actual. Esta variable se reinicia automáticamente a cero cada vez que se inicia un nuevo archivo.NF
: Número de campos. Contiene el número de campos en el registro de entrada actual. El último campo en el registro de entrada puede ser designado por $NF, el 2o a último campo por $(NF-1), el 3o a último campo por $(NF-2), etc.FILENAME
: Contiene el nombre del fichero de entrada actual.FS
Separador de Campo. Contiene el separador de campo utilizado para dividir campos en el registro de entrada. El "espacio blanco" predeterminado permite cualquier secuencia de caracteres de espacio y pestaña. FS puede ser reasignado con otra secuencia de caracteres o caracteres para cambiar el separador de campo.RS
: Separador de grabación. Almacena el actual personaje "separador de discos". Dado que, por defecto, una línea de entrada es el registro de entrada, el personaje de separador de registro predeterminado es una "newline".OFS
: Separador de campo de salida. Almacena el "separador de campo de salida", que separa los campos cuando Awk los imprime. El predeterminado es un personaje "espacio".ORS
: Separador de Registro de salida. Almacena el "separador de registro de salida", que separa los registros de salida cuando Awk los imprime. El predeterminado es un personaje "newline".OFMT
: Formato de salida. Almacena el formato para salida numérica. El formato predeterminado es "%.6g".
Variables y sintaxis
Los nombres de las variables pueden usar cualquiera de los caracteres [A-Za-z0-9_], con la excepción de las palabras clave de idioma. Los operadores + - * / representan suma, resta, multiplicación y división, respectivamente. Para la concatenación de cadenas, simplemente coloque dos variables (o constantes de cadena) una al lado de la otra. Es opcional usar un espacio en el medio si se trata de constantes de cadena, pero dos nombres de variables colocados uno al lado del otro requieren un espacio en el medio. Las comillas dobles delimitan las constantes de cadena. Las declaraciones no necesitan terminar con punto y coma. Finalmente, se pueden agregar comentarios a los programas usando # como el primer carácter en una línea.
Funciones definidas por el usuario
En un formato similar a C, las definiciones de funciones constan de la palabra clave function
, el nombre de la función, los nombres de los argumentos y el cuerpo de la función. Aquí hay un ejemplo de una función.
función add_three ()Número) {} retorno Número + 3}
Esta declaración se puede invocar de la siguiente manera:
()patrón) {} impresión add_three()36) # Outputs ''39' '}
Las funciones pueden tener variables que están en el ámbito local. Los nombres de estos se agregan al final de la lista de argumentos, aunque los valores para estos deben omitirse al llamar a la función. Es una convención agregar algunos espacios en blanco en la lista de argumentos antes de las variables locales, para indicar dónde terminan los parámetros y comienzan las variables locales.
Ejemplos
Hola Mundo
Aquí está el habitual "Hola, mundo" programa escrito en AWK:
BEGIN {} impresión "¡Hola, mundo!" Salida}
Líneas de impresión de más de 80 caracteres
Imprime todas las líneas de más de 80 caracteres. Tenga en cuenta que la acción predeterminada es imprimir la línea actual.
longitud()$0) ■ 80
Contar palabras
Cuente las palabras en la entrada e imprima el número de líneas, palabras y caracteres (como wc):
{} palabras += NF chars += longitud + 1 # add one to account for the newline character at the end of each record (line)}FIN {} impresión NR, palabras, chars }
Como no hay un patrón para la primera línea del programa, cada línea de entrada coincide de forma predeterminada, por lo que las acciones de incremento se ejecutan para cada línea. Tenga en cuenta que words += NF
es una abreviatura de words = words + NF
.
Suma última palabra
{} s += $NF }FIN {} impresión s + 0 }
s se incrementa por el valor numérico de $NF, que es la última palabra en la línea definida por el separador de campo de AWK (por defecto, espacios en blanco). NF es el número de campos en la línea actual, p. 4. Dado que $4 es el valor del cuarto campo, $NF es el valor del último campo de la línea, independientemente de cuántos campos tenga esta línea o si tiene más o menos campos que las líneas circundantes. $ es en realidad un operador unario con la mayor precedencia de operadores. (Si la línea no tiene campos, entonces NF es 0, $0 es la línea completa, que en este caso está vacía aparte de posibles espacios en blanco, al igual que el valor numérico 0.)
Al final de la entrada, el patrón END coincide, por lo que se imprime s. Sin embargo, dado que puede que no haya ninguna línea de entrada, en cuyo caso nunca se ha asignado ningún valor a s, por defecto será una cadena vacía. Agregar cero a una variable es un modismo AWK para forzarlo de una cadena a un valor numérico. (Concatenar una cadena vacía es forzar de un número a una cadena, por ejemplo, s "". Tenga en cuenta que no hay ningún operador para concatenar cadenas, ellos' se colocan de forma adyacente). Con la coerción, el programa imprime "0" en una entrada vacía, sin ella, se imprime una línea vacía.
Hacer coincidir un rango de líneas de entrada
NR % 4 == 1, NR % 4 == 3 {} printf "%6d%sn", NR, $0 }
La declaración de acción imprime cada línea numerada. La función printf emula el estándar C printf y funciona de manera similar al comando de impresión descrito anteriormente. Sin embargo, el patrón para hacer coincidir funciona de la siguiente manera: NR es el número de registros, generalmente líneas de entrada, que AWK ha leído hasta ahora, es decir, el número de línea actual, comenzando en 1 para la primera línea de aporte. % es el operador módulo. NR % 4 == 1 es verdadero para las líneas de entrada 1, 5, 9, etc. Del mismo modo, NR % 4 == 3 es cierto para las líneas de entrada 3, 7, 11, etc. El patrón de rango es falso hasta que la primera parte coincida, en la línea 1, y luego permanece verdadero hasta e inclusive cuando la segunda parte coincida, en la línea 3. Luego permanece falso hasta que la primera parte coincida nuevamente en la línea 5.
Por lo tanto, el programa imprime las líneas 1, 2, 3, salta la línea 4 y luego la 5, 6, 7 y así sucesivamente. Para cada línea, imprime el número de línea (en un campo de 6 caracteres de ancho) y luego el contenido de la línea. Por ejemplo, cuando se ejecuta en esta entrada:
Roma Florencia Milan Nápoles Turín Venecia
El programa anterior imprime:
1o de Roma 2 Florencia 3 Milan 5 Turín 6 Venecia
Impresión de la parte inicial o final de un archivo
Como caso especial, cuando la primera parte de un patrón de rango es constantemente cierta, p. 1, el rango comenzará al principio de la entrada. De manera similar, si la segunda parte es constantemente falsa, p. 0, el rango continuará hasta el final de la entrada. Por ejemplo,
/^--cortado aquí--$/, 0
imprime líneas de entrada desde la primera línea que coincide con la expresión regular ^--corte aquí--$, es decir, una línea que contiene solo la frase "--corte aquí-- ", hasta el final.
Calcular frecuencias de palabras
Frecuencia de palabras usando matrices asociativas:
BEGIN {} SM="[^a-zA-Z]+"}{} para ()i=1; i.NF; i++) palabras[más lento()$i)++}FIN {} para ()i dentro palabras) impresión i, palabras[i]}
El bloque BEGIN establece el separador de campo en cualquier secuencia de caracteres no alfabéticos. Tenga en cuenta que los separadores pueden ser expresiones regulares. Después de eso, llegamos a una simple acción, que realiza la acción en cada línea de entrada. En este caso, por cada campo de la línea, sumamos uno al número de veces que aparece esa palabra, primero convertida a minúsculas. Finalmente, en el bloque END, imprimimos las palabras con sus frecuencias. La línea
para (i en palabras)
crea un bucle que recorre la matriz palabras, estableciendo i en cada subíndice de la matriz. Esto es diferente de la mayoría de los lenguajes, donde dicho bucle pasa por cada valor en la matriz. Por lo tanto, el ciclo imprime cada palabra seguida de su conteo de frecuencia. tolower
fue una adición a One True awk (ver más abajo) realizada después de la publicación del libro.
Patrón de coincidencia desde la línea de comando
Este programa se puede representar de varias formas. El primero usa el shell de Bourne para hacer un script de shell que hace todo. Es el más corto de estos métodos:
#/bin/shpatrón="1 dólar"cambioawk '/ '"$pattern"'/ { print FILENAME ":" $0 ' "$@"
El $pattern
en el comando awk no está protegido por comillas simples, por lo que el shell expande la variable, pero debe colocarse entre comillas dobles para manejar correctamente los patrones que contienen espacios. Un patrón por sí mismo de la forma habitual comprueba si la línea completa ($0
) coincide. FILENAME
contiene el nombre de archivo actual. awk no tiene un operador de concatenación explícito; dos cadenas adyacentes los concatenan. $0
se expande a la línea de entrada original sin cambios.
Hay formas alternativas de escribir esto. Este script de shell accede al entorno directamente desde awk:
#/bin/shExportación patrón="1 dólar"cambioawk '$0 ~ ENVIRON["pattern] { print FILENAME ":" $0 } ' "$@"
Este es un script de shell que usa ENVIRON
, una matriz introducida en una versión más reciente de One True awk después de la publicación del libro. El subíndice de ENVIRON
es el nombre de una variable de entorno; su resultado es el valor de la variable. Esto es como la función getenv en varias bibliotecas estándar y POSIX. El script de shell crea una variable de entorno pattern
que contiene el primer argumento, luego descarta ese argumento y hace que awk busque el patrón en cada archivo.
~
comprueba si su operando izquierdo coincide con su operando derecho; !~
es su inversa. Tenga en cuenta que una expresión regular es solo una cadena y se puede almacenar en variables.
La siguiente forma utiliza la asignación de variables en la línea de comandos, en la que un argumento para awk puede verse como una asignación a una variable:
#/bin/shpatrón="1 dólar"cambioawk '$0 ~ patrón { print FILENAME ":" $0 } ' "pattern=$pattern" "$@"
O puede usar la opción de línea de comando -v var=value (por ejemplo, awk -v pattern="$pattern"...).
Finalmente, esto está escrito en awk puro, sin la ayuda de un shell o sin la necesidad de saber demasiado sobre la implementación del script awk (como lo hace la asignación de variables en la línea de comandos), pero es un poco largo:
BEGIN {} patrón = ARGV[1] para ()i = 1; i . ARGC; i++) # Remove first argument ARGV[i] = ARGV[i + 1] ARGC-- si ()ARGC == 1) {} # the pattern was the only thing, so force read from standard input (used by book) ARGC = 2 ARGV[1] = "- }}$0 ~ patrón {} impresión FILENAME ":" $0 }
El BEGIN
es necesario no solo para extraer el primer argumento, sino también para evitar que se interprete como un nombre de archivo después de que finalice el bloque BEGIN
. ARGC
, el número de argumentos, siempre se garantiza que sea ≥1, ya que ARGV[0]
es el nombre del comando que ejecutó el script, generalmente la cadena "awk". También tenga en cuenta que ARGV[ARGC]
es la cadena vacía, ""
. #
inicia un comentario que se expande hasta el final de la línea.
Observe el bloque if
. awk solo verifica si debe leer desde la entrada estándar antes de ejecutar el comando. Esto significa que
awk 'prog '
solo funciona porque el hecho de que no hay nombres de archivo solo se comprueba antes de ejecutar prog
. Si establece explícitamente ARGC
en 1 para que no haya argumentos, awk simplemente se cerrará porque siente que no hay más archivos de entrada. Por lo tanto, debe decir explícitamente que lea desde la entrada estándar con el nombre de archivo especial -
.
Scripts AWK autónomos
En los sistemas operativos similares a Unix, los scripts AWK autónomos se pueden construir utilizando la sintaxis shebang.
Por ejemplo, una secuencia de comandos que imprime el contenido de un archivo determinado puede crearse creando un archivo llamado print.awk
con el siguiente contenido:
#/usr/bin/awk -f{} impresión $0 }
Se puede invocar con: ./print.awk <filename>
El -f
le dice a AWK que el argumento que sigue es el archivo desde el que leer el programa AWK, que es el mismo indicador que se usa en sed. Dado que a menudo se usan para frases ingeniosas, ambos programas ejecutan de forma predeterminada un programa dado como un argumento de línea de comandos, en lugar de un archivo separado.
Versiones e implementaciones
AWK se escribió originalmente en 1977 y se distribuyó con la versión 7 de Unix.
En 1985, sus autores comenzaron a expandir el lenguaje, sobre todo al agregar funciones definidas por el usuario. El lenguaje se describe en el libro El lenguaje de programación AWK, publicado en 1988, y su implementación estuvo disponible en versiones de UNIX System V. Para evitar confusiones con la versión anterior incompatible, esta versión a veces se llamaba & #34;nuevo awk" o desagradable. Esta implementación fue lanzada bajo una licencia de software libre en 1996 y todavía la mantiene Brian Kernighan (ver enlaces externos a continuación).
Las versiones antiguas de Unix, como UNIX/32V, incluían awkcc
, que convertía AWK en C. Kernighan escribió un programa para convertir awk en C++; se desconoce su estado.
- BWK awk, también conocido como nawk, se refiere a la versión de Brian Kernighan. Se ha anotado el "Uno Verdadero AWK" debido al uso del término en asociación con el libro que originalmente describió el idioma y el hecho de que Kernighan era uno de los autores originales de AWK. FreeBSD se refiere a esta versión como one-true-awk. Esta versión también tiene características no en el libro, como
tolower
yENVIRON
que se explican anteriormente; ver el archivo FIXES en el archivo fuente para obtener detalles. Esta versión es utilizada por, por ejemplo, Android, FreeBSD, NetBSD, OpenBSD, macOS y illumos. Brian Kernighan y Arnold Robbins son los principales contribuyentes a un repositorio de fuentes para nawk: github.com/onetrueawk/awk. - Gawk (GNU awk) es otra aplicación de software libre y la única aplicación que hace progresos serios en la aplicación de la internacionalización y localización y la creación de redes TCP/IP. Se escribió antes de que la aplicación original se pusiera en libertad. Incluye su propio depurador, y su perfilador permite al usuario hacer mejoras de rendimiento medidos a un script. También permite al usuario ampliar la funcionalidad con bibliotecas compartidas. Algunas distribuciones de Linux incluyen Gawk como su implementación predeterminada de AWK. A partir de la versión 5.2 (septiembre 2022) Gawk incluye una característica de memoria persistente que puede recordar variables y funciones definidas por script desde una invocación de un script al siguiente y pasar datos entre scripts no relacionados, como se describe en la Memoria Persistente Gawk Manual de usuario: www.gnu.org/software/gawk/manual/pm-gawk/.
- gawk-csv. La extensión CSV de Gawk Proporciona instalaciones para el manejo de datos de entrada y salida CSV formateados.
- Mawk es una aplicación AWK muy rápida por Mike Brennan basado en un intérprete de código secundario.
- libmawk es un tenedor de mawk, lo que permite a las aplicaciones incrustar múltiples casos paralelos de intérpretes awk.
- awka (cuyo extremo delantero está escrito encima de la Mawk programa) es otro traductor de scripts AWK en código C. Cuando se compilan, estadísticamente incluyendo la libawka.a del autor, los ejecutables resultantes son considerablemente aumentados y, según las pruebas del autor, comparar muy bien con otras versiones de AWK, Perl o Tcl. Los pequeños scripts se convertirán en programas de 160-170 kB.
- tawk (Thompson AWK) es un compilador AWK para Solaris, DOS, OS/2 y Windows, previamente vendido por Thompson Automation Software (que ha cesado sus actividades).
- Jawk es un proyecto para implementar AWK en Java, alojado en SourceForge. Se agregan extensiones al idioma para proporcionar acceso a funciones Java dentro de scripts AWK (es decir, hilos Java, sockets, colecciones, etc.).
- xgawk es un tenedor Gawk que se extiende Gawk con bibliotecas dinámicamente cargables. La extensión XMLgawk se integró en la versión oficial GNU Awk 4.1.0.
- QSEAWK es una aplicación integrada de intérpretes AWK incluida en la biblioteca QSE que proporciona interfaz de programación de aplicaciones de integración (API) para C y C++.
- libfawk es un intérprete muy pequeño, funcional, reentrante, embedible escrito en C
- BusyBox incluye una aplicación AWK escrita por Dmitry Zakharov. Esta es una aplicación muy pequeña adecuada para sistemas integrados.
- CLAWK por Michael Parker proporciona una implementación AWK en Common Lisp, basada en la biblioteca de expresión regular del mismo autor.
Libros
- Aho, Alfred V.; Kernighan, Brian W.; Weinberger, Peter J. (1988-01-01). El lenguaje de programación AWK. Nueva York, NY: Addison-Wesley. ISBN 0-201-07981-X. Retrieved 2017-01-22.
- Robbins, Arnold (2001-05-15). Programación eficaz de awk (3rd ed.). Sebastopol, O'Reilly Media. ISBN 0-596-00070-7. Retrieved 2009-04-16.
- Dougherty, Dale; Robbins, Arnold (1997-03-01). sed " awk (2nd ed.). Sebastopol, O'Reilly Media. ISBN 1-56592-225-5. Retrieved 2009-04-16.
- Robbins, Arnold (2000). Effective Awk Programming: Guía de usuario para Gnu Awk (1.0.3 ed.). Bloomington, IN: iUniverse. ISBN 0-595-10034-1. Archivado desde el original el 12 de abril de 2009. Retrieved 2009-04-16.
Contenido relacionado
C (lenguaje de programación)
Oberón (lenguaje de programación)
Irsi