awk

¡Esta es una revisión vieja del documento!


awk es un lenguaje de análisis y procesado orientado a patrones de texto. Está diseñado para hacer sencilla las tareas de recopilación común de información y manipulación de texto.

La operación básica de awk es analizar un conjunto de líneas de entrada en orden, buscando por coincidencias que se han especificado. Para cada patrón, puede especificarse una acción; esta acción será llevada a cabo en cada línea que muestre coincidencias.

En la práctica, el uso de awk suele caer en dos amplias categorías. Una es la que podría llamarse “generación de informes”: procesado de una entrada para extraer conteos, sumas, subtotales, etcétera. Esto también incluye la escritura de programas de validación de datos triviales, como verificar que un campo contiene únicamente información numérica o que ciertos delimitadores están adecuadamente balanceados.

Una segunda área de uso es como transformador de datos, conviertiendo datos de la forma producida por un programa en la esperada por otro distinto. Los ejemplos más simples consisten en simplemente seleccionar campos, y tal vez reorganizarlos.

awk resulta invaluable para el el procesado textual y numérico combinado

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

patrón { acción }

A su turno, cada línea de entrada resulta analizada por awk contra cada patrón. En cada uno de los patrones en los que encuentre coincidencia, awk ejecutará la acción solicitada. Una vez que awk ha finalizado de evaluar todos los patrones solicitados en la línea, cargará la siguiente línea, y comenzará nuevamente el análisis de la misma.

En cada declaración del programa pueden omitirse tanto el patrón como acción, pero no pueden omitirse ambas. Si un patrón carece de acción, la línea coincidente simplemente se presenta como salida (por lo tanto una linea con muchos patrones coincidentes puede resultar impresa varias veces). Si una acción carece de patrones, entonces dicha acción se se realizará para cada línea de entrada.

Naturalmente, las líneas cuyos patrones no tienen coincidencia, resultan ignoradas (“filtradas”).

Como en las declaraciones de awk tanto los patrones como acciones resultan opcionales, como forma de distinguir unos de otras, son las acciones las que se encierran entre llaves {}.

La entrada de awk se divide en registros finalizados por un separador de registro. El separador de registro por defecto es un carácter ASCII nulo de nueva línea. En cristiano, esto significa que de modo que por defecto, awk procesa la entrada de a una línea por vez. El número del registro actual se dispone en una variable denominada NR.

Cada registro se considera dividido en campos. Los campos normalmente están separados por un carácter de espacio (en blanco o de tabulación). Pero el separador de campo de entrada es asingable. Los campos se refieren como $1, $2, etcétera, siendo $1 el primer campo, $2 el segundo, etc, y donde $0 representa todo el registro de entrada.

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

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

En caso que el separador de registro esté vacío, se considera como separador de registro por defecto a a una línea vacía, y los caracteres en blanco, tabuladores y nuevas líneas se consideran como separadores de campo por defecto.

La variable FILENAME contiene el nombre del fichero de entrada actual.

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

[print $3; $2]

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

$2 ~/A|B|C/]

Este imprime todas las líneas en las cuales el primer campo es diferente que el primer campo previo:

$2 ~/A|B|C/]

El comando

awk programa [ficheros]

ejecuta los comandos de awk en la cadena programa sobre el conjunto de ficheros especificxados, o en la entrada estándar si no hubiese ficheros. La declaración también puede colocarse en un fichero_salida con el comando:

awk -f fichero_salida [ficheros]

Impresión

Una acción puede carecer de patrón, en cuyo caso la acción se ejecutará para todas las líneas. La acción más simple es imprimir algo o todo de de un registro; esto se realiza con el comando print de awk.

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

{ print }

Más útil es imprimir un campo o campos de cada registro, por ejemplo, imprimir los priermos dos campos en orden inverso:

print $2, $1

Los ítems separados por , en la declaración de impresión resultarán separados por el separador de campo de salida actual, cuando se les de salida. Los ítems que no estén separados por comas en la entrada, resultarán concatenados. De este modo, para correr el primer y segundo campos juntos, se utiliza:

print $1 $2

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

{ print NR, NF, $0 }

La salida puede divirise en múltiples ficheros, por ejemplo el programa siguiente escribe el primer campo $1 en fichero1 y el segundo campo en el fichero2.

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

También puede usarse la notación » para agregar la salida al fichero1 (se lo creará de ser necesario).

print $1 >>"fichero1"

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

print $1 >$2

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

print | "mail bwk"

envía por correo electrónico la salida a bwk.

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

Awk también provee a la declaración printf para formateado de salida, con esta sintaxis:

printf expresión, expresión, ....

Por ejemplo, para imprimir $1 como una cifra de coma flotante de 8 cifras, con dos luego de la coma, y $2 como una cifra decimal de 10 dígitos, seguida por una nueva línea, se usaría:

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

Un patrón en frente de una acción opera como un selector que determina si la acción debe ejecutarse. Pueden utilizarse una variedad de expresiones como patrones: expresiones regulares, expresiones relacionales aritméticas, expresiones de cadena, y combinaciones booleanas arbitrarias.

BEGIN y END

El patrón especial BEGIN coincide con el comienzo de la entrada, antes de que sea leído el primer registro. El patrón END coincide con el final de la entrada, después de que el registro ha sido procesado. Por lo tanto, BEGIN y END proveen una forma de inicialización o obtención de control previo, y luego de procesar, como indicativo de cedido de control final.

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

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

Naturalmente, si BEGIN está presente en el primer patrón, END debe aparecer en el último utilizado.

La expresión regular más simple es la cadena literal de caracteres, cerrada entre barras /, como /perez/

Esto representa realmente un programa completo de awk que imprimirá todas las líneas que contienen cualquier ocurrencia del nombre perez. Si una línea contiene perez como parte de una palabra más grande, también resultará coincidente, como perezoso.

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

Por ejemplo, este programa de awk:

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

…imprimirá todas las líneas que contengan cualquiera de los nombres ‘‘Abad,’’ ‘‘Wenceslao’’ o ‘‘Karina,’’ estén en mayúsculas o no.

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

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

También podrá especificar que cualquier campo o variable busque coincidencia de una expresión regular (o no coincida con ella, utilizano los operadores y !∼.

Por ejemplo, este programa de awk imprime todas las líneas donde el primer campo coincide con juan o Juan. Note que esto también coincidirá con Juana, San Juan, etcétera.

$1/[jJ]uan/

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

$1/ˆ[jJ]uan$/

El circunflejo ˆ refiere al comienzo de la línea o campo, mientras que el signo pesos $ refderirá al final de línea o campo.

Expresiones Relacionales de awk

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

NF % 2 == 0

…imprime las líneas con el un número de campos par.

Sin embargo, si ninguno de los operandos sometidos a evaluación relacionar fuese numérico, se realizará una comparación de cadenas de caracteres alfabéticos.

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

$1 >= "s"

En la ausencia de cualquier otra información que permita dilucidar el contexto, se asumen los campos como cadenas alfabéticas. De esta forma, este programa realizará una comparación de cadena alfabética:

$1 > $2

Patrones Combinacionales

Un patrón puede ser cualquier patrones combinacinoal booleanos OR ||, AND &&, y NOT !.

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

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

Rangos de Patrones

El patrón que selecciona una acción puede consistir de dos patrones separados por ,, siguiendo la sintaxis

patrón1, patrón2 { ... }

En este caso, la acción se realiza para cada línea entre una ocurrencia de patron1 y la siguiente ocurrencia de patron2 (inclusive). Por ejemplo, /empezar/, /detener/ imprime todas las líneas existentes entre las cadenas empezar y denener, mientras que NR == 100, NR == 200 { … } realiza la acción desde la línea 100 hasta la línea 200 de la entrada.

Una acción de awk consiste en una secuencia de declaraciones de acción finalizadas por un caracter nulo de nueva línea o bien un ;.

Dichas declaraciones de acción pueden utilizarse para realizar una variedad de tareas de manipulación de cadenas o de registro.

Awk incorpora la función lenght para computar la longitud de una cadena de caracteres. Este programa imprimirá cada registro, precedido por su longitud:

{print length, $0}

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

{print length($0), $0}

Awk también incorpora las funciones aritméticas raíz cuadrada sqrt, logaritmo log, exponencial exp, e integral int, como parte de sus respectivos argumentos.

El nombre de dichas funciones incorporadas, sin argumento o entre paréntesis, significarán un valor de función para el registro entero.

Este programa imprime líneas cuya longitud sea inferior a 10 o mayor que 20:

length < 10 || length > 20

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

La función incorporada index(c1, c2) devuelve el índice binario donde la cadena c2 ocurre en c1, resultando 0 si no lo hace.

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

Por ello, en el ejemplo,

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

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

Variables, Expresiones, y Asignaciones

Las variables de awk toman valores numéricos (coma flotante) o cadenas alfabéticas de acuerdo al contexto. Por ejemplo, en x = 1, x es claramente una cifra, mientras que en x = “perez” es claramente una cadena alfanumérica. Las cadenas se convierten a números y viceversa, toda vez que el contexto lo demande. Por ejemplo:

x = "3" + "4"

…asigna 7 a la x.

Las cadenas que no pueden ser intepretadas numéricamente en un contexto numérico generalmente tienen un valor numérico de 0, pero no suele ser adecuado contar siguiendo este comportamiento.

Por defecto las demás variables que no son las incorporadas, deben inicializarse a una cadena nula, que tiene un valor numérico de 0; esto elimina la necesidad de la mayoría de las secciones de tipo BEGIN. Por ejemplo, la sima de los primeros dos campos puede computarse con:

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

La aritmética se hace internamente en coma flotante. Los operadores ariméticos son +, , , /, y % (porcentaje o módulo).

También están disponibles los operadores de C para incremento ++ y decremento ––, así como los operadores de asignación +=, –=, ∗=, /=, y %=. Estos operadores pueden utilizarse en las expresiones.

Los campos en awk comparten escencialmente todas las propiedades de las variables: puedeb usarse en artimética u operaciones de cadenas, y también pueden ser asignados. Por lo tanto puede reemplazarse el primer campo con un número de secuencia: Fields in awk share essentially all of the properties of variables — they may be used in arithmetic

{ $1 = NR; print }

…o acumular dos campos en un tercero, así:

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

…o bien asignar una cadena a un campo:

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

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

Pueden usarse expresiones numéricas para referenciar campos:

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

…donde la consideración de campo como numérico o cadena depende del contexto; en casos ambiguos como if ($1 == $2) … los campos son tratados como cadenas.

Cada línea de entrada se divide en campos automáticamente de ser necesario. También es posible dividir cualquier variable o cadena en campos:

n = split(c, arreglo, sep)

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

Las cadenas pueden concatenarse. Por ejemplo:

length($1 $2 $3)

…revuelve la longitud de los primeros tres campos. O en una declaración impresa:

print $1 " es " $2

…imprime los dos campos separados por “ es ”. Las variables y expresiones numéricas también pueden aparecer en las concatenaciones.

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

x[NR] = $0

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

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

La primer acción meramente registra cada línea de entrada en el arreglo x.

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

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

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

Primero se evalúa la condición entre paréntesis (); si es verdadera, se ejecuta la declaración siguiente a if. La sección else es opcional.

La declaración while procede como en C. Para imrpirmir todos los campos de entrada uno por línea:

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

La declaración for opera como en C:

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

…hace lo mismo que la declaración while explicada. Existe una forma alternativa para la declaración for que se adecúa para acceder a los elementos de un arreglo asociativo:

for (i in array)
statement

…donde la declaración i activa cada elemento del arreglo. Los elementos son accedidos en un órden aparentemente aleatorio. Surgirá el caos is se altera el i, o si cualquier elemento nuevo se accede durante el bucle.

La expresión en la sección de la condición de un if, while or for puede incluir operadores relacionales como <, , >, >=, == (“igual a”), y != (“no igual a”); coincidencias de expresiones regulares con operadores de coincidencia y !∼; los operadores lógicos ||, &&, y !; y por supuesto las paréntesis () para agruparlos.

La declaración break provoca la salida inmediata de un while o for encerrado; la declaración continue provoca el cimienzo de la siguiente iteración.

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

Pueden disponerse comentarios en los programas de awk: comienzan con el caracter # y finalizan con el final de la línea, como en

print x, y # Esto es un comentario de línea.

Analiza cada fichero de entrada en búsqueda de coincidencias con conjunto de patrones dados. Estos pueden ser especificados literalmente en forma de programación, o a través de uno o más ficheros de guiones específicados.

En cada patrón puede existir una acción asociada a realizar, no bien una línea del fichero coincida con el patrón. Cada línea es analizada en búsqueda de coincidencias de cada declaración patrón-acción; la acción asociada se realiza allí. El nombre de fichero - singnifica entrada estándar. Cualquier fichero de la forma variable-valor será tratado como asignación, no un nombre de fichero, y se ejecuta al tiempo en que se hubiese abierto si fuese un fichero.

La entrada normalmente está conformada por líneas de entrada (registros), o bien por el valor de RS. Si el RS es nulo, las líneas en blanco se utilizan como separadores de registro, y las líneas se usan como separadores de campo (además del valor de FS). Esto resulta conveniente al trabajar con registros multilíneas.

En las líneas de entrada, los campos resultan divididos por espacios en blancos, o bien por el caracter valor designado como separador de campo FS al leer la línea. Los campos se designan $1, $2, …, ($0 refiere a la línea completa).

El separador de campo FS puede ser un caracter simple, o una expresión regular. Como caso especial, si FS es un espacio en blanco (su opción por defecto), los campos serán divididos por uno o más caracterers en blanco. Si el FS es nulo, la línea de entrada se dividi en un campo por caracter.

Los campos pueden separarse normalmente por cualquier cantidad de caracteres en blanco. Para establecer un separador de campo como un espacio en blanco simple, debe usarse la opción -F con un valor de [ ]. Para especificar un caracter de tabulador como separador de campo, debe indicarse /t o bien t

El separador de campo se configura usualmente vía la opción -F o desde el bloque BEGIN, de modo que toma efecto antes de que se lea la entrada.

Una declaración patrón-acción guarda la siguiente sintaxis:

patrón { acción }

Si no se provee una { acción}, se imprimirá la línea. Si no se provee un patrón, siempre habrá coincidencias. Las declaración patrón-acción deben ponerse en líneas separadas, o separarse con ;.

Se permiten nuevas líneas luego de una declaración de finalización, o siguiento una ,, una { o un AND lógico (&&) o un OR lógico (||)., luego de las instrucciones do o else, o luego del ) de las declaraciones if, for o while. Además, la \ puede utilizarse para anular el significado interpretado de nua nueva línea entre las declaraciones patrón-acción.

Una acción de awk es una secuencia de declaraciones.

Las declaraciones pueden ser una de las siguientes:

  • Declaración if (expresión) [declaración else]
  • Declaración while (expresión)
  • Declaración for (expreión; expresión; expresión)
  • Declaración for (var en array)
  • Declaración do while (expresión)
  • break
  • continue
  • { [declaración ...] }
  • expression (conmunmente var = expression
  • print [expresión-lista] [>expression]
  • printf format […, expresión-lista] [>expresión]
  • return [expresión]
  • next (saltea los patroners remanentes de esta línea de entrada)
  • nextfile (saltea lo siguiente en este fichero, abre el siguiente, comienza desde arriba)
  • delete array[expression] (borra un elemento del array)
  • delete array (borra todos los elementos del array)
  • exit [expresión] (sale del procesado y realiza procesado END; el status será expresión)

Las declaraciónes deben finalizarse con ;, fin de línea o }. Una expresión-lista vacía corresponde a $0. Las constantes de cadena se ponen entre “”, y se reconocen los escapes usuales del lenguaje C. Las expresiones tomas una cadena o valores numéricos, y se construyen con los operadores + - * / % ^. Los operadores ! ++ += -= *= /= %= ^= > >= < <= == != y ?: también se usan en las expresiones.

Las variables pueden ser escalares, elementos de arrays (denotamos como x o campos). Las variaables se inicializan en la cadena nula. Los arrays subscriptos pueden ser cualquier cadena, no necesaria numéricas; esto permite una forma de memoria asociativa. Se permiten los múltiples subcritos como [i,j,k], sus constituyentes son concatenados, separados por el valor de SUBSEP

Print

La declaración print presenta sus argumentos en la salida estándar (o en un fichero utilizando los operadores > o » en un caño si se utiliza | comando, separado por el separador de campo de salida, y terminado por el separador de registro de salida.

Los ficheros con comandos puedes ser nombres literales o (expresion); los valores de cadena idénticos en declaraciones diferentes denotan al mismo fichero abierto. La declaración printf formatea su lista de expresión de acuerdo al formato.

Los patrones son combinaciones booleanas arbitrarias (con ! || && or expresiones regulares y expresiones relacionales. awk soporta expresiones regulares extendidas.

Las expresiones regulares aisladas en un patrón se aplican a la línea entera. Estas pueden ocurrir en las expresiones relacionales cuando utilizando los operadores ~ y !~. /re/ es una expresión constante regular; puede utilizarse cualquier cadena (constante o variable) como una expresión regular, con excepción de la posición de una expresión regular aislada en un patrón.

Un patrón puede consistir de dos patrones separados por ,; en tal caso, la acción se desarrolla para todqas las líneas desde la primer ocurrencia del patrón a través de las ocurrencias del segundo.

Una expresión relacional puede cobrar una de las siguientes formas:

  • expresión matchop expresión-regular
  • expresión relop expression
  • expresión en nombre-array
  • (expr, expr, …) en nombre-array

donde una relop es cualquiera de los operadores relacionales en C, y un matchop es ya sea un ~ (coindidencia) o !~ (no coincidencia). Un condicional es una expresión aritmética, una expresión relacional, o una combinación booleana de estas.

El patrón especial BEGIN puede utilizarse para capturar conrtol antes de que la primera línea de entrada sea leída. El patrón especial END puede ser utilizado para capturar control luego de que el procesado sea finalizado. BEGIN y END no pueden combinarse con otros patrones. Pueden aparecer en múltiples ocasiones en un programa, y ejecutarse en el orden que son leídos por awk.

Nombres de las variables de awk.

ARGC Contaror de aumento, asignable.
ARGV Array de automento, asignable; números no nulos resultan consideredos nombres de fichero.
CONVFMT Formato de conversión cuando se convierten números (por defecto %.6g?).
ENVIRON Array de variables de ambiente; los subscriptos son nombres.
FILENAME El nombre del fichero de entrada actual.
FNR Número ordinal del registro actual en el fichero actual.
FS Expresión regular utilizada para separar campos (por defecto espacios en blanco); también seteable a través de la opción -F fs.
NF Cantidad de campos en el registro actual. Puede usarse $NF para obtener el valor del último campo en el registro actual.
NR Número ordinal del registro actual.
OFMT Formato de salida para los números (por defecto %.6g).
OFS Separador del campo de salida (por defecto carácter en blanco)).
ORS Separador del registro de salida (por defecto nueva línea).
RLENGTH El largo de la cadena en búsqueda de coincidencia por la función match().
RS Separador del registro de entrada (por defecto nueva línea). Si está vacío, las líneas en blanco separan registros. Si hay mas de un carácter en su extensión el RS se trata como una expresión regular, y los registros resultan separados por el texto que coincida con la expresión dada.
RSTART La posición de inicio de la cadena coincidente con la función match().
SUBSEP Separa múltiples suscriptos (por defecto 034).

Funcinoes Aritméticas

Funciones de Cadena

Funciones de tiempo

Funciones de E/S y Generales


EL awk es un poderoso lenguaje de programación que en muchas ocasiones nos sacara de apuros a la hora de tener que hacer script complejos de tratamiento de texto. El awk al igual que el sed lee las líneas completas para realizar sus modificaciones. Uno de los aspectos mas útiles en relación al awk es que a diferencia del sed, awk puede dividir las líneas en campos a través de un separador de campo indicado en el guion o en la línea de comandos. Si no se indica ninguno se tomara como separador de campo un espacio o tabulador. Usando la opción -F de la línea de comandos o la variable FS desde un programa hecho en awk se puede especificar un nuevo separador de campo. Por ejemplo si lo que quisiéramos es ver los nombres verdaderos que aparecen en el archivo /etc/passwd primero tendríamos que saber como separar los campos. En el archivo /etc/passwd se separan por un :. Ahora tendríamos que saber en que campo se encuentra el nombre. Es en el campo numero 5, comenzando a contar como el primero de los campos. El 0 es la línea completa y ya veremos por que

$ cat /etc/passwd | awk -F : '{print $5}'
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
news
uucp
operator
games
gopher
FTP
User
Nobody
X Font Server
Named
PostgreSQL Server
Shrek Ogre
Fiona Ogre

Como vemos lo primero que hicimos fue indicarle al awk cual seria el separador de campo -F :, luego entre comillas le indicamos que imprima a la salida estándar el campo nº 5, '{print $5}'. De esta forma vemos los nombres contenidos en el archivo /etc/passwd. Podríamos imprimir mas de un campo a la vez, por ejemplo si queremos mostrar también el directorio home de cada uno de los usuarios podríamos hacer lo siguiente:

cat /etc/passwd | awk -F : '{print $5,$6}'
root /root
bin /bin
daemon /sbin
adm /var/adm
lp /var/spool/lpd
. . .
PostgreSQL Server /var/lib/pgsql
Shrek Ogre /home/shrek
Fiona Ogre /home/fiona

De esta simple manera podremos ir completando la línea a los requerimientos del campo que queramos ver ya que tenemos la posibilidad de hacer comparaciones a un campo de la misma manera que la haríamos a una variable en cualquier otro lenguaje. Por ejemplo si quisiéramos ver las líneas del /etc/passwd de todos aquellos usuarios que pertenecen al grupo user, representado por el nº 100 en el archivo passwd, podríamos hacer que el awk comprara el número del campo en el que esta el número GUID que nosotros buscamos. En el caso particular que cada usuario tuviese su grupo, podríamos hacer que se compararan todas las líneas que posean un número mayor o igual al número de grupo de usuarios mas bajo, por ejemplo 500 es el número que por defecto pone Linux a al grupo del primer usuario y va incrementándose a medida que incorporamos usuarios. Tendríamos que mostrar todas las líneas que en campo donde esta el GUID, el número 4, del usuario y que sea mayor o igual a 500. Por ejemplo:

cat /etc/passwd | awk -F :\
'$4>=500 {print $0}'
shrek:x:500:500:Shrek Ogre:/home/shrek:/bin/bash
fiona:x:501:501:Fiona Ogre:/home/fiona:/bin/bash

Como verán se indico que mostrara solo aquellas líneas que tuviesen en el campo nº 4 un valor mayor o igual a 500, $4>=500.También se ve que mostramos la línea entera al poner como campo a imprimir el $0. Una acotación que tendríamos que notar. Lo que comparamos en esta oportunidad es un número y esto lo hace tremendamente poderoso al awk como lenguaje de programación. Si se quisieran compara cadenas, se tendrían que encerrar ente “”. Como ejemplo, si hubiésemos encerrado entre “” al 500 lo que awk interpretaría es que queremos mostrar todas las líneas que en la posición 4 tengan un valor alfabéticamente mayor o igual a 500.

cat /etc/passwd | awk -F :\
'$4>="500" {print $0}'
lp:x:4:7:lp:/var/spool/lpd:
nobody:x:99:99:Nobody:/:
shrek:x:500:500:Shrek Ogre:/home/shrek:/bin/bash
fiona:x:501:501:Fiona Ogre:/home/fiona:/bin/bash

Como verán si se ordena alfabéticamente la posición 4 el 7 y el 99 son mayores que 500. Los operadores que awk puede manejar son los siguientes:

Operadores de Awk

Operador Significado
< menor que
menor que o igual que
== igual a
!= no igual a
>= mayor que o igual que
> mayor que

Otra posibilidad es el usar expresiones regulares para efectuar la búsqueda. Pero cuidado ya que es tienen que ser ingresadas de acuerdo a sí es mayúscula o minúscula.

code bash>cat /etc/passwd | awk -F :\</code>
'/Shrek/ {print $0}'
shrek:x:500:500:Shrek Ogre:/home/shrek:/bin/bash

Lo único que tendremos que hacer es encerrarlo entre / para que se tomen como expresión regular. Ahora bien, las expresiones podrán ser tan complejas como queramos. Por ejemplo si quisiéramos mostrar todas las líneas que tuviesen la cadena se pero que no tengan antes la letra U y no les siga un espacio la orden es

cat /etc/passwd | awk -F :\
'/[^U]se[^ ]/ {print $0}'
shrek:x:500:500:Shrek Ogre:/home/shrek:/bin/bash

Como ven las cadenas que tenemos que ignorar se preceden antes y después de la cadena buscada ingresando un símbolo ^ encerrado entre []. De esta manera se podrá ir usando las distintas expresiones regulares. En todos estos casos se utilizaron una única forma para imprimir en pantalla los resultados, pero es bueno saber que contamos con otra forma en la que podremos formatear el texto antes de su salida por pantalla. Para la salida formateada se utiliza el printf. Por ejemplo si quisiéramos podríamos imprimir los datos en una forma más cómoda:

cat /etc/passwd | awk -F :\
'$4>=500 {printf"%20s %5s\n",$5,$1}'
Shrek Ogre shrek Fiona Ogre fiona

Como se puede ver, pedimos que nos mostrara el nombre completo de l usuario y el nombre de usuario. Como sabemos la extensión aproximada que tendrá cada campo le damos 20 posiciones para le primer campo a mostrar, el $5, y 5 posiciones para el segundo campo a mostrar, el $1. Si lo que quisiéramos mostrar fuesen número en lugar de la %s (string) iría una %d o %i (decimal). Para mas información sobre el printf buscar en las páginas de manual del awk. Existen diferentes variables integradas, a parte del FS, que permiten llevar cuentas de distintos aspectos. Por ejemplo existe la variable NR que llevara la cuenta de los registros que mostremos. Por ejemplo supongamos que necesitamos obtener un listado largo de un directorio, pero solo queremos ver los permisos, el nombre del archivo y el número de registro que a pasado por el awk.

ls -l | awk '{ print NR" "$1" "$9}'
1 total
2 -rw-rw-r-- 146768
3 -rw-rw-r-- Bienvenidos
4 -rw-rw-r-- Bienv
5 -rw-rw-r-- authkey.file
6 drwxr-xr-x Desktop
7 -rw-rw-r-- LUGRo
8 drwxrwxr-x Linux
9 -rw-rw-r-- Listado
10 -rw-rw-r-- Lo
11 drwx------ Mail
. . .
49 -rw-rw-r-- pgaccess-report.ps
50 -rw-rw-r-- sed
51 -rw-rw-r-- sed.zip
52 -rw-rw-r-- smtptel.htm
53 -rw-rw-r-- vicky 

Como verán, en esta ocasión la variable NR fue llevando la cuenta de los registros que fueron pasando por el awk. De esta forma se podrá decirle al awk que me muestre de los registros 5 al 10 solamente. scr Existen muchas más variables en el awk que son de extrema utilidad. Por ejemplo, en el caso anterior sabíamos que el ultimo campo estaba en la posición número 9, pero ¿que ocurre si no sabemos la posición del último campo o esta varia? Para esto esta la variable NF que lleva la cuenta de la cantidad de campos de cada registro. Por ello en lugar de la anterior forma podríamos poner:

ls -l | awk '{ print NR" "$1" "$NF}'

y obtendríamos idénticos resultados. Pero un momento, aquí hay algo raro. La variable NR no tiene el signo $, en cambio la variable NF si lo tiene. Esto esta dado así para que no se reemplazado por el awk. Por ejemplo si hubiésemos puesto la variable NF sin signo $ el resultado seria.

ls -l | awk '{ print NR" "$1" "NF}'
1 total 2
2 -rw-rw-r-- 9
3 -rw-rw-r-- 11
4 -rw-rw-r-- 11
5 -rw-rw-r-- 11
6 drwxr-xr-x 9
. . . 

Lo que nos esta mostrando no es el último campo, sino la cantidad de campos que ese registro tiene. Al agregarle el signo $ se reemplazara con el número del último campo y ese campo el que será mostrado. Esto es así para todas las variables integradas. El awk puede ser usado no-solo en una línea. Podríamos usarlo también como cualquier otro lenguaje para realizar múltiples tareas en una línea o realizar algo antes de comenzar la lectura y otra después. Para demarcar el código se utiliza los pares BEGIN-END. Todo lo que aparece después de la palabra BEGIN, pero en el mismo renglón, se realiza antes de que comience el ciclo. Cualquier cosa que este después de END se realiza después de que se haya leído la última línea y cerrado el ciclo. Estas líneas tendrán que estar en un archivo que será utilizado por el awk para procesar en este ejemplo al archivo /etc/passwd. Un ejemplo seria el siguiente:

BEGIN { FS=":"}
{ printf"Nombre Completo: %s\n",$5 }
{ printf"Nombre de Usuario: %s\n",$1}
{ printf"UID: %i,GUID: %i\n\n",$3,$4 }
END { printf "\n\nTotal de usuarios: %d \n\n", NR}

Este pequeño programa realizado con el vi será guardado en el archivo awk.src, el nombre se lo damos nosotros, y nos servirá para mostrar algunos datos del /etc/passwd mas detalladamente. La forma de ejecutarlo es a través del modificado “-f” donde le decimos al awk que a continuación le pasaremos un archivo con el programa que tiene que usar para procesar el /etc/passwd.

awk -f awk.src /etc/passwd
Nombre Completo: root Nombre de Usuario: root UID: 0,GUID: 0
Nombre Completo: bin Nombre de Usuario: bin UID: 1,GUID: 1
Nombre Completo: daemon Nombre de Usuario: daemon UID: 2,GUID: 2
Nombre Completo: adm Nombre de Usuario: adm UID: 3,GUID: 4
. . .
Nombre Completo: PostgreSQL Server Nombre de Usuario: postgres UID: 26,GUID: 26
Nombre Completo: Shrek Ogre Nombre de Usuario: shrek UID: 500,GUID: 500
Nombre Completo: Fiona Ogre Nombre de Usuario: fiona UID: 501,GUID: 501
Total de usuarios: 22

Para finalizar diremos que también se podrán hacer operaciones con estas variables, es decir, sumarlas restarlas, multiplicarlas y dividirlas. Por ejemplo si quisiéramos saber en cuantos bloques de 4 líneas podríamos formar con un archivo de texto dado podríamos hacer el siguiente programa:

BEGIN {FS=":"}
{ print $0 }
FNR%4==0 { printf"\n" }
END { printf "El archivo %s puede entrar en %i bloques enteros\
 de 4 lineas\n",FILENAME,NR/4}

Existen un par de cosas nueva en el. Por ejemplo la variable FNR que cuenta el número de líneas. En esta ocasión le estamos diciendo que si el modulo de FNR es igual a 4 FNR%4 imprima un salto de linea { printf“\n” }. Y al finalizar el ciclo se mostrara el mensaje que nos informara cuantos bloques de 4 líneas podremos tener. Para obtener el resultado se efectúa una división del numero total de registros NR por el número de líneas que queremos armar el bloque, 4. La variable FILENAME indica cual es el archivo sobre el cual se realizo el proceso y es tomada cuando se la pasamos al awk como argumento. El resultado es

awk -f awk2.src /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:
daemon:x:2:2:daemon:/sbin:
adm:x:3:4:adm:/var/adm:
lp:x:4:7:lp:/var/spool/lpd:
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:
news:x:9:13:news:/var/spool/news:
uucp:x:10:14:uucp:/var/spool/uucp:
operator:x:11:0:operator:/root:
games:x:12:100:games:/usr/games:
gopher:x:13:30:gopher:/usr/lib/gopher-data:
ftp:x:14:50:FTP User:/home/ftp:
nobody:x:99:99:Nobody:/:
xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false
named:x:25:25:Named:/var/named:/bin/false
gdm:x:42:42::/home/gdm:/bin/bash
postgres:x:26:26:PostgreSQL Server:/var/lib/pgsql:/bin/bash
shrek:x:500:500:Shrek Ogre:/home/shrek:/bin/bash
fiona:x:501:501:Fiona Ogre :/home/fiona:/bin/bash

El archivo /etc/passwd puede entrar en 5 bloques enteros de 4 lineas

  • awk.1681262378.txt.gz
  • Última modificación: 2023/04/12 01:19
  • por peron