Inicio » Posts tagged 'Awk'

Tag Archives: Awk

Eliminando la primera columna de un archivo con AWK

Cuando empezaba a escribir programas en mi otro blog, numeraba las filas y exportaba el archivo como html desde el editor Vim. Esto causaba que los programas se vieran más o menos así:

 1
 2
 3   // QUE HACE EL SIGUIENTE PROGRAMA?
 4 
 5 
 6   #include <iostream>
 7   using namespace::std;
 8 
 9 
10  void Alguna_Funcion ( char  b[], int Tamano )
11 
12  {
13   if ( Tamano > 0 )
14   {
15   Alguna_Funcion ( &b[1], Tamano - 1 );
16   cout << b[0] << " ";
17   }
18  }
19 
20 
21  int main()
22  {
23  const int Tamano_Arreglo = 4;
24  char A[Tamano_Arreglo] = {'H', 'O', 'L', 'A' };
25 
26  cout <<"\n\nLos valores en el arreglo son: " << endl;
27 
28  Alguna_Funcion(A, Tamano_Arreglo);
29  cout << endl;
30 
31  return 0;
32 
33  }

Esto hace fácil que uno pueda hacer referencia a una línea en particular a la hora de describir el código, sin embargo le deja al lector la tarea de suprimir la numeración de las filas para poder compilar y ejecutar el programa. Esto parece una tarea fácil, y lo es para éste ejemplo; sin embargo yo mismo tuve que hacerla para este programa que contiene más de mil líneas. Eso ya no es divertido. Además, es muy fácil alterar el código: eliminar un punto y coma, un paréntesis, cuando se hace esta labor a mano.
jarmvel me ayudó con un pequeño script en awk que me sirvió. Sin embargo, como no soy muy ordenado, y cambié de disco duro, tuve que reinventar la rueda. En realidad lo hice de una manera bastante burda, pero directa:

{print $2,$3,$4,$5,$6,$7,$8,$9,$10,$11}

Esta línea es un programa simple en el lenguaje de programación Awk.
Todo el cuerpo del programa debe de ir encerrado entre llaves {}. Dentro, la primera instrucción es print, esta es la orden de imprimir, y lo siguiente son los argumentos de print: $2,…$11 El signo $ seguido de un número hace referencia a una columna. La columna $0 hace referencia a toda la línea. Por defecto, awk toma como columna todo lo que hay en una fila desde la aparición del primer carácter no blanco (no tabulador, no espacio) hasta la aparición del primer espacio. Así que en el código de arriba, para awk, el renglón 3 tiene 7 columnas, la línea 10, 9 y la línea 33, 2. El programa en Awk lo que hace es evaluar todas las líneas del archivo que se le dé como entrada e imprime la segunda columna, la tercera, … hasta la once. ¿Por qué hasta la columna 11? Porque en este caso, la línea 26 tiene 11 columnas. Si sólo llegáramos hasta $10 se imprimiría la esa línea, sin la numeración hasta <<.
Ahora, ¿cómo ejecutar el programa en Awk?
La forma más fácil es la siguiente:

awk '{print $2,$3,$4,$5,$6,$7,$8,$9,$10,$11}' ProgramaIncognita.cpp

En la línea de comandos se escribe el comando awk seguido por el programa encerrado en comillas sencillas ‘ ‘, y luego el archivo que se da de entrada. La salida es la estándar.
Otra forma es guardando la línea en un archivo, digamos Programa.awk. Y luego invocarlo de la siguiente manera:

awk -f Programa.awk ProgramaIncognita.cpp

la opción -f le indica a awk que se tomarán las instrucciones del archivo cuyo nombre se escribe a continuación. Con cualquiera de las dos formas la salida es:

         
         
// QUE HACE EL SIGUIENTE PROGRAMA?    
         
         
#include <iostream>        
using namespace::std;        
         
         
void Alguna_Funcion ( char b[], int Tamano )  
         
{         
if ( Tamano > 0 )    
{         
Alguna_Funcion ( &b[1], Tamano - 1 );   
cout << b[0] << " ";    
}         
}         
         
         
int main()        
{         
const int Tamano_Arreglo = 4;     
char A[Tamano_Arreglo] = {'H', 'O', 'L', 'A' };  
         
cout <<"\n\nLos valores en el arreglo son: " << endl;
         
Alguna_Funcion(A, Tamano_Arreglo);        
cout << endl;       
         
return 0;        
         
}         

El mismo programa en C++ del principio, ahora sin la numeración al comienzo de cada línea.
Sólo para que nadie se muera de curiosidad, la salida del programa Incognita.cpp es la siguiente:

Los valores en el arreglo son: 
A L O H 

Un Tutorial de AWK

EL PRIMER PROGRAMA EN AWK
Los usuarios de las computadoras pasan mucho tiempo realizando tareas de manipulación de datos que son mecánicas y laboriosas, cambiando el formato de un archivo, encontrando elementos con cierta característica, añadiendo números, etc., y sin embargo sería impensable realizar un programa en C o Pascal para realizar esas tareas cada vez que se presentan.
Más o menos con estas palabras presentan Alfred V. Aho, Brian W. Kernighan y Peter J. Weinberger su libro The AWK Programming Language. Esta entrada, que se corresponde con los temas y la estructura de el primer capítulo, está basada principalmente en ese libro. El lenguaje AWK fue creado para realizar esas tareas.

Supongamos que tenemos un archivo llamado ArchivoAwk.dat con el contenido siguiente:

Jose    4.00   0
Pedro   7.00   1
Rosa   23.30   5
Maria  28.10   6
Juan   29.32   0

ArchivoAwk.dat contiene los nombres de 5 personas (Jose, Pedro, Rosa, Maria, Juan), el salario por hora que recibien (4.00, 7.00, 23.30, 28.10, 29.32) y el número de horas que han trabajado (0, 1, 5, 6, 0). El lenguaje de programación AWK sólo reconoce esos dos tipos de datos: números y cadenas de caracteres. Supongamos que queremos imprimir el nombre y el salario total (número de horas por el salario por hora) de todas las personas que laboraron más de 0 horas. Ese es el tipo de trabajo para el que AWK está hecho. Basta con escribir lo siguiente en la consola:

awk '$3 > 0 {print $1, $2 * $3}' ArchivoAwk.dat

Y se obtendrá como salida

Pedro 7
Rosa 116.5
Maria 168.6

La instrucción consta de 3 elementos: 1) una llamada al comando Unix AWK, 2) un programa y 3) el archivo de entrada.
1) basta con imprimir el comando awk
2) Todo lo que está contenido dentro de las comillas simples (‘) es el programa en AWK
3) El archivo se enlaza simplemente escribiendo su nombre o ruta completa.
La llamada a AWK y al archivo no tienen mayor complicación. El programa requiere una explicación adicional.
La primera parte es un condicional. La condición en lenguaje humano es:

¿Es el valor de la columna 3 mayor que 0?

La condición en lenguaje AWK es

 $3 > 0 

Para hacer referencia a una columna usamos el signo de peso $. Así, $3 hace referencia a la columna 3, $1 hace referencia a la columna 1; $0 hace referencia a TODA la línea. En particular $3 a lo que vale la columna 3 en el renglón actual.
Lo que viene a continuación (encerrado entre llaves) es lo que ha de realizarse en caso de que la condición se cumpla:

{print $1, $2 * $3}

La instrucción print (imprimir, en inglés) es clara. Se debe imprimir algo. ¿Qué se imprime? Lo que viene justo a continuación, es decir, el valor de la columna 1, o sea el nombre del trabajador y el salario, el cual es el producto del salario por hora (columna 2) por el número de horas (columna 3). En AWK el signo para indicar multiplicación es el asterisco (*). La coma que aparece justo después de el número 1, indica que ha de dejarse un espacio en blanco entre el valor de la columna 1 y el producto de las columnas 2 y 3. AWK repite la instrucción entre corchetes para todas las líneas del archivo, por lo tanto el resultado desplegado arriba muestra, como queríamos, el salario de los trabajadores que sí recibirán una paga.
¿Que pasa si más bien queremos imprimir los nombres de las personas que no recibirán salario?
Basta con escribir lo siguiente:

awk '$3 == 0 {print $1}' ArchivoAwk.dat

lo cual da como salida

Jose
Juan

De nuevo, esta instrucción consta de tres partes: 1) La llamada a awk, 2) el programa y 3) el archivo
Tanto 1) como 2) son fáciles de llamar y enlazar. 3) Requiere de una explicación.
El programa en AWK es todo lo que aparece encerrado entre comillas simples (‘). Ese programa a su vez está dividido en 2 partes, un condicional y una instrucción. La condición es, en lenguaje humano

¿Es el valor de la columna 3 igual a 0?

Y esa instrucción, convertida a lenguaje AWK es

 $3 == 0 

Ya hemos visto que para hacer referencia a una columna, necesitamos usar el símbolo de pesos $. El número 3 nos dice que se trata de la tercera. Lo siguiente es también claro, el signo doble igual (==) se usa en muchos lenguajes de programación (C y derivados, por ejemplo) para indicar comparación, por contraposición a el simple igual (=), que se usa para asignación. Muchos problemas pueden surgir si se se confunden los signos = y == en esos lenguajes. Tantos problemas causa esa confusión, que el creador de Pascal, un lenguaje diseñado para estudiantes, decidió que las asignaciones, en ese lenguaje, deberían de hacerse con el símbolo :=, lo cual evita estos errores sutiles. AWK usa == para comparaciones. Así pues, la instrucción en AWK equivale a la instrucción en español.
Si tenemos además otro archivo llamado ArchivoAwk2.dat con otra lista de trabajadores y salarios similar a la anterior

Ernesto  12    0
Raquel    5    3
Gabriela  3    5
Carolina  3    8

podemos imprimir los salarios para las dos listas simplemente con modificar la llamada:

awk '$3 >0 {print $1, $2*$3}' ArchivoAwk.dat ArchivoAwk2.dat

y la salida que se obtiene es

Pedro 7
Rosa 116.5
María 168.6
Raquel 15
Gabriela 15
Carolina 24

PARA EJECUTAR UN PROGRAMA EN AWK

Hay varias formas:
Escribiendo en línea de comandos una orden del tipo

awk 'programa' archivos de entrada

Escribiendo la misma orden, pero sin archivos de entrada:

awk 'programa'

En este caso el programa se ejecutará sobre todo lo que se teclee a continuación hasta la aparición de una tecla de fin de archivo (EOF) Ctrl + d para los Unix.
Creando un archivo que contenga el programa.
Si el programa es chico, entonces es práctico escribirlo directamente en la línea de comandos, mientras que si se trata de un programa grande, conviene escribirlo en un editor de texto y guardarlo en un archivo con un nombre arbitrario. Supongamos que queremos guardar el programa anterior en un archivo llamado Programa.awk (el nombre es totalmente arbitrario, y la extensión también. Yo uso .awk para fines de administración solamente) Ese archivo contiene lo que antes pusimos entre comillas simples ‘.

$3 >0 {print $1, $2*$3}

Para ejecutarlo necesitamos simplemente escribir la siguiente línea:

awk -f Programa.awk ArchivoAwk.dat

Esta instrucción consta de 3 partes principales 1) la llamada a awk, 2) la llamada al código fuente del programa y 3) El enlace al archivo que queremos analizar.
1) y 3) ya son conocidos. 2) Requiere una explicación.
Para llamar al código fuente simplemente debemos agregar la opción -f antes del nombre del archivo que contiene el código. La opción -f avisa que el programa se recibirá de un archivo, y no de la línea de comandos. Eso es todo. El resultado es el mismo que se presenta arriba.
IMPRESIONES SIMPLES
Como se ha dicho, AWK sólo tiene dos tipos de datos: números y cadenas de caracteres. Cada línea es separada por AWK en campos, siendo cada campo el número o la cadena completa que aparecen sin un espacio en blanco de por medio.
IMPRIMIR CADA LÍNEA
Con la simple instrucción print en el cuerpo del programa, se presentan todas las líneas del archivo

 awk '{print}' ArchivoAwk.dat

Otra forma de imprimir cada línea completa es mediante la referencia al campo $0, que como vimos es el nombre de toda la línea.

awk '{print $0}' ArchivoAwk.dat

En ambos casos el resultado es el mismo:

Jose    4.00   0
Pedro   7.00   1
Rosa   23.30   5
Maria  28.10   6
Juan   29.32   0

IMPRESIÓN DE ALGUNOS CAMPOS
Es posible imprimir sólo algunos campos de la línea, por ejemplo la columna 1 y la columna 3 mediante la instrucción:

awk '{print $1, $3}' ArchivoAwk.dat

la cual produce la salida

Jose 0
Pedro 1
Rosa 5
Maria 6
Juan 0

Es bueno recordar que la coma después del 1 deja un espacio en blanco. En caso de faltar, las dos columnas aparecerían juntas.
NF, EL NÚMERO DE CAMPOS
Awk cuenta el número de columnas de la presente línea y lo almacena en una variable llamada NF.
Por ejemplo, si el ArchivoAwk.awk

Jose    4.00   0
Pedro   7.00   1
Rosa   23.30   5
Maria  28.10   6
Juan   29.32   0

aplicamos el código

awk -f'{print NF,$NF}' ArchivoAwk.dat

el resultado es:

3 0
3 1
3 5
3 6
3 0

En cada caso presentando el número de columnas de cada línea y a continuación el último elemento.
NR, EL NÚMERO DE lÍNEA
La variable NR guarda el número de línea actual. Como ejemplo, en el ArchivoAwk.dat se puede indicar el número de renglón mediante la línea

awk '{printf NR, $0}' ArchivoAwk.dat

y se produce la salida

1 Jose    4.00   0
2 Pedro   7.00   1
3 Rosa   23.30   5
4 Maria  28.10   6
5 Juan   29.32   0

IMPRESIÓN DE TEXTO
La línea

awk '{print "El pago total para " $1 " es: " $2*$3}' ArchivoAwk.dat

imprime:

El pago total para Jose es: 0
El pago total para Pedro es: 7
El pago total para Rosa es: 116.5
El pago total para Maria es: 168.6
El pago total para Juan es: 0

Así que para imprimir una cadena con print es necesario incluir la cadena entre comillas.
IMPRESIÓN CON FORMATO
La siguiente línea es un ejemplo de uso de la instrucción printf

awk '{printf("El pago total para %s es: %.2f\n", $1, $2*$3)}' ArchivoAwk.dat

y el resultado es el siguiente:

El pago total para Jose es: 0.00
El pago total para Pedro es: 7.00
El pago total para Rosa es: 116.50
El pago total para Maria es: 168.60
El pago total para Juan es: 0.00

El formato de printf es muy parecido a el que tiene en el lenguaje de Programación C:
Todas las cadenas que aparezcan dentro de las comillas dobles se imprimen literalmente. Las excepciones son las secuencias de escape (\n, salto de línea). %s hace referencia a la cadena que se encuentra en la columna 1, $1; %.2f hace referencia a que se imprimirá un número flotante con 2 decimales después del punto ($2*$3).

El Usuario toor en FreeBSD

Revisando el archivo /etc/passwd en FreeBSD y ejecutando el script de seguridad

awk -F: '{if ($3==0) print $1}' /etc/passwd

encuentro que existe un usuario llamado toor, que yo no he añadido, y que además tiene privelegios de root. Si mi sistema fuera linux debería estar preocupado, pero FreeBSD es un poco diferente.
En realidad toor es un usuario incluído por el propio sistema ( toor es un anagrama de root ) como un superusuario alternativo, al cual se le puede asignar una contraseña desde root. toor es, más o menos, una alternativa a la opción de booteo Single User Mode.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 1.789 seguidores