Edición de archivos con awk#
awk
es un comando para Unix creado para el procesamiento y reporte de archivos de texto que contengan varios campos de datos en una misma línea. Se considera que awk
es una evolución de sed
. Su uso básico es:
awk [condición] '{printf"formato", argumentos}' filename
Manual y ayuda en línea#
Ayuda:
man awk
Algunos comandos importantes#
$0
-> Todos los camposFS
-> Separador de campos (TAB por defecto)NF
-> Número de campos en la linea actualNR
-> Número de lineas en el archivo a procesarLength
-> Longitud de la linea a procesar&&
-> Operación lógica para la intersección||
-> Operación lógica para la unión
Impresión de líneas específicias#
[1]:
%%bash
# apt-get update
# apt-get install gawk
[2]:
%%bash
#
# se imprimen los primeros 30 números a un archivo
#
seq 30 > out.1
[3]:
%%bash
#
# imprime la línea 3
#
awk 'NR == 3 {print}' out.1
3
[4]:
%%bash
#
# imprime las líneas 3 a 6
#
awk '(NR >= 3) && (NR <= 6) {print $0}' out.1
3
4
5
6
[5]:
%%bash
#
# imprime las líneas 3 a 6 y 11 a 13
#
awk '((NR >= 3) && (NR <= 6) || ((NR >= 11) && (NR <= 13))) {print $0}' out.1
3
4
5
6
11
12
13
Filtrado#
[6]:
%%bash
#
# Imprime las lineas que contengan un 1
#
awk '/1/ {print}' out.1
1
10
11
12
13
14
15
16
17
18
19
21
La cadena /1/
indica que la línea contenga un 1
, los /
son delimitadores y la cadena {print}
al final indica que se imprima la línea.
[7]:
%%bash
#
# Imprime las líneas que tengaun un 1 al final
#
awk '/1$/ {print}' out.1
1
11
21
[8]:
%%bash
#
# Imprime las líneas que tengan un 1 al inicio
#
awk '/^1/ {print}' out.1
1
10
11
12
13
14
15
16
17
18
19
Sustitución#
En el archivo generado a continuación se desea substituir la X
por x
:
[9]:
%%bash
cat > out.1 <<EOF
FieldA, fieldD, gieldE, FieldG
2, X, 2X, 2XG
2, Y, 2Y, 2YG
3, Y, 3Y, 3YG
3, X, 3X, 3XG
4, Z, 4Z, 3XG
EOF
awk
permite realizar sustituciones mediante el comando gsub()
compuesto por una expresión regular que busca el patrón y el elemento que reemplazará o modificará el elemento encontrado. La g
indica que es global, en caso de que se requiera una sustitución local se debe utilizar la función sub()
.
En el siguiente código se sustituye únicamente la primera ocurrencia en cada línea del archivo:
[10]:
%%bash
#
# sustituye la primera ocurrencia de X por x en cada línea
#
awk '{sub(/X/, "x"); print}' out.1
FieldA, fieldD, gieldE, FieldG
2, x, 2X, 2XG
2, Y, 2Y, 2YG
3, Y, 3Y, 3YG
3, x, 3X, 3XG
4, Z, 4Z, 3xG
Si se desean reemplazar todas las ocurrencias se usa gsub
.
[11]:
%%bash
#
# Sustituye todas las ocurrencias de X por x
#
awk '{gsub(/X/, "x"); print}' out.1
FieldA, fieldD, gieldE, FieldG
2, x, 2x, 2xG
2, Y, 2Y, 2YG
3, Y, 3Y, 3YG
3, x, 3x, 3xG
4, Z, 4Z, 3xG
Se puede utiliza el comando gsub
para varias sustituciones separandolo por ;
:
[12]:
%%bash
awk '{gsub(/X/, "x"); gsub(/Y/, "y"); print}' out.1
FieldA, fieldD, gieldE, FieldG
2, x, 2x, 2xG
2, y, 2y, 2yG
3, y, 3y, 3yG
3, x, 3x, 3xG
4, Z, 4Z, 3xG
Extracción#
La función split
permite separar strings e incluir sus elementos en un array, el cual se recorre de acuerdo con la posición. Sigue la siguiente estructura:
split($0,nombre_arreglo,"separador")
Sea el siguiente archivo:
[13]:
%%bash
cat > out.1 <<EOF
AAA-BBB-CCC
DDD-EEE-FFF
GGG-HHH-III
EOF
La segunda columna puede extraerse con:
[14]:
%%bash
#
# Parte la linea por los caracteres indicados y la asigna a una variable
#
awk '{split($0,nombre,"-")} {print nombre[2]}' out.1
BBB
EEE
HHH
A continuación se presenta un ejemplo más complejo de modificación de un patrón. Sea el siguiente archivo:
[15]:
%%bash
cat > out.1 <<EOF
Maria-1998:feb:2+M19
David-1972:nov:25+J45
Marco-2000:jun:4+V17
EOF
Se desea formatear la fecha de nacimiento completa de la persona y su edad, es decir, la primera línea:
Maria-1998:feb:2+M19
debe cambiarse por:
Maria 1998-02-02 M 19
La función gensub
permite realizar busquedas a través de expresiones regulares y reemplazar dichos valores por elementos a elección de acuerdo con la frecuencia de coincidencia del patrón.
El primer paso consiste en reemplazar :
por -
.
[16]:
%%bash
cat out.1
Maria-1998:feb:2+M19
David-1972:nov:25+J45
Marco-2000:jun:4+V17
[17]:
%%bash
awk '{print gensub(/:([a-zA-Z]*)/,"-""\\1",1)}' out.1 > out.2
cat out.2
Maria-1998-feb:2+M19
David-1972-nov:25+J45
Marco-2000-jun:4+V17
La explicación del comando anterior es la siguiente:
Patrón entrada
El caracter
/
indica el inicio y el fin de la expresión regular.Los caracteres
(
y)
contienen la expresión, caracter o dígito que se desea guardar.[a-z]
indica una cadena.El caracter
*
indica que existen cadenas luego de la expresión regular.
Patrón salida * \\\1
primer elemento de la busqueda que fue guardado.
Frecuencia * 1
se debe reemplazar cuando encuentre el patrón por primera vez.
[18]:
%%bash
awk '{print gensub(/:([0-9])/, "-\\1", 1)}' out.2 > out.3
cat out.3
Maria-1998-feb-2+M19
David-1972-nov-25+J45
Marco-2000-jun-4+V17
[19]:
%%bash
awk '{print gensub(/-([0-9])*/, "-0\\1", 3)}' out.3 > out.4
cat out.4
Maria-1998-feb-02+M19
David-1972-nov-05+J45
Marco-2000-jun-04+V17
[20]:
%%sh
awk '{print gensub(/+([A-Z])([0-9][0-9])/, " \\1 \\2", 1)}' out.4 > out.5
cat out.5
Maria-1998-feb-02 M 19
David-1972-nov-05 J 45
Marco-2000-jun-04 V 17
[21]:
%%bash
awk '{sub(/-/, " "); print}' out.5 > out.6
cat out.6
Maria 1998-feb-02 M 19
David 1972-nov-05 J 45
Marco 2000-jun-04 V 17
[22]:
%%bash
awk '{gsub(/feb/, "02"); gsub(/nov/, "11"); gsub(/jun/, "06"); print}' out.6
Maria 1998-02-02 M 19
David 1972-11-05 J 45
Marco 2000-06-04 V 17
Agregación de campos#
Sea el siguiente archivo:
[23]:
%%bash
cat > out.1 <<EOF
Date, Price, Quantity, CustomerID
2013-01-12, 25, 7, 1
2014-05-12, 41, 5, 12
2013-02-25, 44, 3, 2
2013-04-04, 90, 1, 5
2013-06-21, 16, 2, 19
2014-05-12, 63, 2, 15
2014-05-12, 10, 4, 7
2013-02-28, 78, 8, 9
2013-08-02, 51, 1, 14
EOF
Se desea agregar un nuevo campo llamado Quantity-CustomerID
que contenga la cantidad de producto y el cliente. El siguiente comando une las columnas de interés:
[24]:
%%bash
awk '{print gensub(/, ([0-9][0-9]), ([0-9]), ([0-9])/, ", \\1, \\2-\\3", 1)}' out.1 > out.2
cat out.2
Date, Price, Quantity, CustomerID
2013-01-12, 25, 7-1
2014-05-12, 41, 5-12
2013-02-25, 44, 3-2
2013-04-04, 90, 1-5
2013-06-21, 16, 2-19
2014-05-12, 63, 2-15
2014-05-12, 10, 4-7
2013-02-28, 78, 8-9
2013-08-02, 51, 1-14
Se agrega el título Quantity-CustomerID
:
[25]:
%%bash
awk '{print gensub(/([a-zA-Z]*), ([a-zA-Z]*), ([a-zA-Z]*), ([a-zA-Z]*)/, "\\1, \\2, \\3-\\4", 1)}' out.2
Date, Price, Quantity-CustomerID
2013-01-12, 25, 7-1
2014-05-12, 41, 5-12
2013-02-25, 44, 3-2
2013-04-04, 90, 1-5
2013-06-21, 16, 2-19
2014-05-12, 63, 2-15
2014-05-12, 10, 4-7
2013-02-28, 78, 8-9
2013-08-02, 51, 1-14
A través del comando BEGIN
y END
se pueden agregar valores al principio y al final de las columnas o de todo el archivo de texto.
El comando NR>1
indica que se debe tener en cuenta solo filas a partir de la posicion 1 y {print $0}
que se deben tener en cuenta todas las columnas.
[26]:
%%bash
awk 'BEGIN{print "Date, Price, Quantity-CustomerID"}(NR>1){print $0}' out.2
Date, Price, Quantity-CustomerID
2013-01-12, 25, 7-1
2014-05-12, 41, 5-12
2013-02-25, 44, 3-2
2013-04-04, 90, 1-5
2013-06-21, 16, 2-19
2014-05-12, 63, 2-15
2014-05-12, 10, 4-7
2013-02-28, 78, 8-9
2013-08-02, 51, 1-14
[27]:
%%bash
awk '(NR>1){print $3} END{print "Hola"}' out.2
7-1
5-12
3-2
1-5
2-19
2-15
4-7
8-9
1-14
Hola
Se desea agregar un nuevo campo llamado Quantity*Price
que contenga el total de la cuenta de cada compra. El comando {print $1 $2}
concatena e imprime columnas iniciando el conteo en 1 de izquierda a derecha.
[28]:
%%bash
awk -F"," '{print $1","$2","$3", "$4", "$2*$4}' out.1 > out.2
cat out.2
Date, Price, Quantity, CustomerID, 0
2013-01-12, 25, 7, 1, 25
2014-05-12, 41, 5, 12, 492
2013-02-25, 44, 3, 2, 88
2013-04-04, 90, 1, 5, 450
2013-06-21, 16, 2, 19, 304
2014-05-12, 63, 2, 15, 945
2014-05-12, 10, 4, 7, 70
2013-02-28, 78, 8, 9, 702
2013-08-02, 51, 1, 14, 714
Ahora, se debe agregar el título a la nueva columna:
[29]:
%%bash
awk '{print gensub(/, 0/, ", Total", 1)}' out.2
Date, Price, Quantity, CustomerID, Total
2013-01-12, 25, 7, 1, 25
2014-05-12, 41, 5, 12, 492
2013-02-25, 44, 3, 2, 88
2013-04-04, 90, 1, 5, 450
2013-06-21, 16, 2, 19, 304
2014-05-12, 63, 2, 15, 945
2014-05-12, 10, 4, 7, 70
2013-02-28, 78, 8, 9, 702
2013-08-02, 51, 1, 14, 714
Se puede utilizar el comando la opción -F
y OFS
para cambiar el separador del archivo de texto:
[30]:
%%bash
awk -F"," 'BEGIN{OFS="|";}{print $1,$2,$3,$4}' out.1
Date| Price| Quantity| CustomerID
2013-01-12| 25| 7| 1
2014-05-12| 41| 5| 12
2013-02-25| 44| 3| 2
2013-04-04| 90| 1| 5
2013-06-21| 16| 2| 19
2014-05-12| 63| 2| 15
2014-05-12| 10| 4| 7
2013-02-28| 78| 8| 9
2013-08-02| 51| 1| 14
Resumen#
## imprime la línea 3
awk 'NR == 3 {print}' out.1
## imprime las líneas 3 a 6
awk '(NR >= 3) && (NR <= 6) {print $0}' out.1
## imprime las líneas 3 a 6 y 11 a 13
awk '((NR >= 3) && (NR <= 6) || ((NR >= 11) && (NR <= 13))) {print $0}' out.1
## Imprime las lineas que contengan un 1
awk '/1/ {print}' out.1
## Imprime las líneas que tengaun un 1 al final
awk '/1$/ {print}' out.1
## Imprime las líneas que tengan un 1 al inicio
awk '/^1/ {print}' out.1
## sustituye la primera ocurrencia de X por x en cada línea
awk '{sub(/X/, "x"); print}' out.1
## Sustituye todas las ocurrencias de X por x
awk '{gsub(/X/, "x"); print}' out.1
## Parte la linea por los caracteres indicados y la asigna a una variable
awk '{split($0,nombre,"-")} {print nombre[2]}' out.1
Borrado de los archivos temporales creados
[31]:
!rm out.*