Este texto se refiere solo a la implementación de GNU de awk conocida como gawk Witch es la más utilizada y viene con cualquier distribución moderna de Linux / Unix .
[La Guía del usuario de GNU AWK] [GNU-AWK] es su referencia, para los ejemplos que utilicé casos del mundo real tomados principalmente de mis respuestas [stackoverflow] [así].
AWK es un lenguaje similar a Perl, solo considerablemente más elegante.
- Arnold Robbins
AWK es un lenguaje de programación diseñado para el procesamiento de texto y generalmente utilizado como herramienta de extracción de datos e informes. Es una característica estándar de la mayoría de los sistemas operativos similares a unix.
awk ...Su nombre se deriva de los apellidos de sus autores: Alfred a Ho, Peter W Einberger y Brian K Ernighan.
awk ...Busca líneas que contengan ciertos patrones en archivos o la entrada estándar.
Se utiliza principalmente para la extracción de datos y los informes, como resumir la información de la salida de otros programas de servicios públicos.
Sintaxis C-like
Impulsado de datos: describa los datos con los que desea trabajar y luego qué acción debe hacer cuando lo encuentre.
pattern { action }
pattern { action }Si el programa es corto :
awk ' program ' input-file1 input-file2NOTA: Tenga cuidado con los problemas de cita de shell 1 .
cmd | awk ' program ' Nota: La pipe redirige la salida del comando de la izquierda ( cmd ) a la entrada del comando awk 2 .
Cuando el código es largo , generalmente es más conveniente ponerlo en un archivo y ejecutarlo con un comando como este:
awk -f program-file input-file1 input-file2
cmd | awk -f program-file O simplemente hazlo ejecutable como un shebang :
#! /bin/awk -f
BEGIN { print " hello world!! " } -F fs Establece la variable FS en fs .
-v var=val establece la variable var al valor val antes de que comience la ejecución del programa.
Nota : Se puede usar más de una vez, configurando otra variable cada vez.
Estos patrones especiales o bloquean las acciones de inicio y limpieza de suministros para programas awk .
BEGIN{
// initialize variables
}
{
/pattern/ { action }
}
END{
// cleanup
}BEGIN antes de que se lea el primer registro de entrada, END después de que se consume la entrada.
$ echo " hello " | awk ' BEGIN{print "BEGIN";f=1}
{print $f}
END{print "END"} '
BEGIN
hello
ENDgrepping si tienes awk ? $ cat lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ grep dolor lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.$ awk ' /dolor/ ' lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.Nota: Si no se le da la acción, la acción predeterminada es imprimir el registro que coincida con el patrón dado.
Pero ... ¿cómo podemos averiguar la primera y última palabra de cada línea?
Por supuesto, grep puede, pero necesita dos pasos:
$ grep -Eo ' ^[^ ]+ ' lorem_ipsum.dat
Lorem
Maecenas
Nunc
Curabitur
Lorem
Aliquam$ grep -Eo ' [^ ]+$ ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
ultrices. Veamos awk en acción aquí:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices. awk divide la entrada para su programa en registros y campos .
Los registros están separados por un personaje llamado Registro RS . Por defecto , el separador de registros es el carácter UNIX Newline n .
Es por eso que los registros son, por defecto , líneas individuales .
Además, awk tiene un separador de registro de salida ORS para controlar la forma en que los registros se presentan al stdout .
RS y ORS deben encerrarse en comillas , que indican una constante de cadena.
Para usar un carácter diferente o un regex , simplemente asigne a las variables RS o / y ORS :
BEGIN de la ejecución, antes de que se procese cualquier entrada, de modo que el primer registro se lea con el separador adecuado.Ejemplos:
$ awk ' BEGIN{RS=" *, *";ORS="<<<---n"}
{print $0} ' lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- $ awk ' {print $0} ' RS= " *, * " ORS= " <<<---n " lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- Los registros awk se analizan o se separan automáticamente en fragmentos llamados campos .
Por defecto, los campos están separados por Whitespace (cualquier cadena de uno o más espacios, pestañas o nuevas líneas), como las palabras en una línea.
Para referirse a un campo en un programa awk , utiliza un signo de dólar $ seguido del número del campo que desea.
Por lo tanto, $1 se refiere al primer campo, $2 a la segunda, y así sucesivamente.
Importante : $0 representa todo el registro de entrada.
$ awk ' {print $3} ' lorem_ipsum.dat
dolor
erat
orci,
dapibus
dolor
mauris NF es una variable predefinida que su valor es el número de campos en el registro actual . Entonces, $NF será siempre el último campo del registro.
$ awk ' {print NF} ' lorem_ipsum.dat
8
7
10
4
8
10vs.
$ awk ' {print $NF} ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
facilisis. FS contiene el valorado del separador de campo , este valor es una cadena de un solo personaje o una regex que coincide con las separaciones entre los campos en un registro de entrada.
El valor predeterminado es " " , una cadena que consta de un solo espacio. Como excepción especial, este valor significa que cualquier secuencia de espacios , pestañas y/o nuevas líneas es un separador único.
De la misma manera que ORS tenemos una variable OFS para administrar cómo se enviarán nuestros campos a la secuencia de salida.
$ cat /etc/group
nobody: * :-2:
nogroup: * :-1:
wheel: * :0:root
daemon: * :1:root
kmem: * :2:root
sys: * :3:root
tty: * :4:root$ awk ' !/^(_|#)/&&$1=$1 ' FS= " : " OFS= " <-> " /etc/group
nobody < - > * < - > - 2< - >
nogroup < - > * < - > - 1< - >
wheel < - > * < - > 0 < - > root
daemon < - > * < - > 1 < - > root
kmem < - > * < - > 2 < - > root
sys < - > * < - > 3 < - > root
tty < - > * < - > 4 < - > root Nota : Ummm ... $1=$1 ???? 3
Teniendo en cuenta los registros y los campos , ahora estaban listos para comprender nuestro código anterior:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices.Estas son dos variables integradas útiles:
NR : Número de registros de entrada awk ha procesado desde el comienzo de la ejecución del programa.
FNR : número de registro actual En el archivo actual, awk restablece FNR a cero cada vez que inicia un nuevo archivo de entrada.
$ cat n1.dat
one
two$ cat n2.dat
three
four$ awk ' {print NR,FNR,$0} ' n1.dat n2.dat
1 1 one
2 2 two
3 1 three
4 2 fourLa cadena de formato es muy similar a la de ISO c .
Sintaxis:
printf format, item1, item2, …
$ awk ' {printf "%20s <-> %sn",$1,$NF} ' lorem_ipsum.dat
Lorem < - > elit.
Maecenas < - > condimentum.
Nunc < - > ex.
Curabitur < - > tellus.
Lorem < - > elit.
Aliquam < - > ultrices. La salida de print y printf se dirige a la salida estándar de forma predeterminada, pero podemos usar la redirección para cambiar el destino.
Las redirecciones en awk están escritas como redirecciones en los comandos de shell , excepto que están escritos dentro del programa awk .
$ awk ' BEGIN{print "hello">"hello.dat"} ' $ awk ' BEGIN{print "world!">>"hello.dat"} ' $ cat hello.dat
hello
world !También es posible enviar la salida a otro programa a través de una tubería :
$ awk ' BEGIN{sh="/bin/sh";print "date"|sh;close(sh)} '
dom nov 13 18:36:25 CET 2016 Las [corrientes] se pueden apuntar al stdin , el stdout y el stderr .
Por ejemplo, podemos escribir un mensaje de error en el stderr como este:
$ awk ' BEGIN{print "Serious error detected!" > "/dev/stderr"} '
Serious error detected ! En AWK, las matrices están asociadas , cada una es una colección de pares , valor índice , donde cualquier número o cadena puede ser un índice.
No se necesita ninguna declaración; Se pueden agregar nuevos pares en cualquier momento.
| Índice | Valor |
|---|---|
| "Perro" | "perro" |
| "Gato" | "gato" |
| "Uno" | "uno" |
| 1 | "uno" |
| 2 | "dos" |
Para referir una matriz:
array[index-expression]
Para asignar valores:
array[index-expression] = value
Para verificar si una clave está indexada:
indx in array
Para iterarlo:
for (var in array) {
var, array[var]
}Uso de valores numéricos como índices y preservando el orden:
for (i = 1 ; i < = max_index ; i++) {
print array[i]
}Un ejemplo completo:
$ cat dict.dat
uno one
dos two
tres three
cuatro fourawk ' {dict[$1]=$2}
END{if ("uno" in dict)
print "Yes we have uno in dict!"
if (!("cinco" in dict))
print "No , cinco is not in dict!"
for (esp in dict){
print esp, "->" ,dict[esp]
}
} ' dict.datTe da:
Yes we have uno in dict !
No , cinco is not in dict !
uno - > one
dos - > two
tres - > three
cuatro - > four gawk no ordena las matrices de forma predeterminada:
awk ' BEGIN{
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 4 four
0 zero
1 one
2 two
3 threePero puede aprovechar [Procinfo] para clasificar:
awk ' BEGIN{
PROCINFO["sorted_in"] = "@ind_num_asc"
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 0 zero
1 one
2 two
3 three
4 four gensub(regexp, replacement, how [, target]) : es la función más avanzada para reemplazar la cadena.
Y sus alternativas más simples:
gsub(regexp, replacement [, target])
sub(regexp, replacement [, target])
Tener este archivo:
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.Vamos a intercambiar la posición de las palabras colocadas a la izquierda y la derecha de cada coma.
$ awk ' {print gensub(/([^ ]+)( *, *)([^ ]+)/,
"\3\2\1", "g")} ' lorem.dat
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim euismod, orci id nisi interdum, eget cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Aliquam interdum mauris volutpat nisl et, placerat facilisis. Usando gensub capturamos tres grupos y luego cambiamos el orden.
Para ilustrar una acción más simple, cambiemos los puntos para las comas :
awk ' $0=gensub(/./, ",", "g") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis, Uso de la alternativa gsub :
awk ' gsub(/./, ",") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis,Esta opción parece mejor cuando no se necesita captura de grupo .
Otras funciones interesantes son index y substr .
index(in, find)
substr(string, start [, length ])
Funciona así:
$ awk ' BEGIN{t="hello-world";print index(t, "-")} '
6$ awk ' BEGIN{t="hello-world";print substr(t,index(t, "-")+1)} '
world La función split se usa para crear una matriz desde una cadena que dividela por un char , devuelve el número de elementos de la matriz creada.
split(string, array [, fieldsep [, seps ] ])
$ cat passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbash$ awk ' n=split($0, a, ":"){print n, a[n]} ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashNota : Esto podría hacerse de una manera mucho más simple:
$ awk ' {print NF,$NF} ' FS= ' : ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashEscribir una función personalizada es bastante simple:
awk ' function test(m)
{
printf "This is a test func, parameter: %sn", m
}
BEGIN{test("param")} 'Danos:
This is a test func, parameter: param También podemos devolver una expresión usando una declaración return :
awk ' function test(m)
{
return sprintf("This is a test func, parameter: %s", m)
}
BEGIN{print test("param")} 'El análisis por parámetro es la única forma de hacer una variable local dentro de una función.
Los valores escalar se pasan por valor y matrices por referencia, por lo que cualquier cambio realizado en una matriz dentro de una función se reflejará en el alcance global:
awk ' function test(m)
{
m[0] = "new"
}
BEGIN{m[0]=1
test(m)
exit
}
END{print m[0]} 'Salidas:
newNuestros desafíos :
01. Penultada palabra de un registro.
02. Reemplazo de un registro.
03. Coloque un punto y coma al final de cada registro.
04. Coloque una coma entre cada palabra.
05. Todos juntos?
06. Redireccionando los registros impares a un archivo e incluso a otro a otro.
07. Dado un archivo de contraseña, obtenga el campo que falta.
08. Intercambio de campo.
09. Hacking de Traceroute.
10. ¿Dónde están mis hijos?
11. Agregación de datos.
12. Registros entre dos patrones.
13. Transformación de campo.
14. Registros a columnas.
15. Procesamiento de archivos FASTA.
16. Informes complejos.
17. FILES APELADOR.
18. Passwd y Group.
19. Conexiones de usuario.
20. Promedio de carga total de tiempo de actividad.
Tener este archivo fuente :
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' {print $(NF-1)} ' lorem.dat
adipiscing
consectetur
cursus
dapibus
adipiscing
neque No es demasiado para explicar aquí, NF almacena el número de campos en el registro actual, por lo que NF-1 apunta al campo antes de last y $(NF-1) será su valor.
Nuestra tarea, sustitución de registros de archivos, la tercera línea debe convertirse:
This not latin
Nada más simple, solo juega con NR ( número de récord ).
Código:
$ awk ' NR==3{print "This is not latin";next}{print} ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. Solución alternativa para evitar next declaración: Asigne la nueva línea al registro completo $0 .
Ejemplo:
$ awk ' NR==3{$0="This is not latin"}1 ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' 1 ' ORS= " ;n " lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Maecenas pellentesque erat vel tortor consectetur condimentum. ;
Nunc enim orci, euismod id nisi eget, interdum cursus ex. ;
Curabitur a dapibus tellus. ;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Aliquam interdum mauris volutpat nisl placerat, et facilisis neque ultrices. ; Como el RS predeterminado es la línea de ruptura UNIX n solo necesitamos prefijar el punto y coma al OFS de registro de salida.
1 ? 4
$ awk ' {$1=$1}1 ' OFS= ' , ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum.
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex.
Curabitur,a,dapibus,tellus.
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. La parte más significativa de este código es cómo obliga a una reconstrucción récord con $1=$1 por el valor actual del OFS .
$ awk ' {$1=$1}1 ' OFS= ' , ' ORS= ' ;n ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum. ;
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex. ;
Curabitur,a,dapibus,tellus. ;
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. ; Tan simplemente como jugando con Vars de salida: OFS y ORS .
Comencemos con la solución final:
$ awk ' NR%2{print > "even.dat";next}
{print > "odd.dat"} ' lorem.dat$ cat even.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Lorem ipsum dolor sit amet, consectetur adipiscing elit$ cat odd.dat
Maecenas pellentesque erat vel tortor consectetur condimentum.
Curabitur a dapibus tellus.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. La función [módulo] ( % ) encuentra el resto después de la división para el número de registro actual NR dividido por dos:
$ awk ' {print NR%2} ' lorem.dat
1
0
1
0
1
0 Hasta donde hasta ahora, en awk 1 es verdadero y 0 falso . Redirigimos nuestra salida evaluando este hecho.
next requiere una atención especial, obliga awk a detener inmediatamente el proceso de registro actual y pasar al siguiente.
De esta manera eludimos una doble condición que se vería así:
awk ' NR % 2{print > "even.dat"}
!NR % 2{print > "odd.dat"} ' lorem.dat$ cat /etc/passwd
jd001:x:1032:666:Javier Diaz::/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez::/bin/rbash
jp003:x:1000:666:Jose Perez::/bin/bash
ms004:x:8051:668:Maria Saenz::/bin/rbash
rc005:x:6550:668:Rosa Camacho::/bin/rbash Supongamos el directorio de inicio prefijando la cadena fija "/home/" al nombre de usuario :
$ awk ' $6="/home/"$1 ' FS= ' : ' OFS= ' : ' /etc/passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbashNuestro primer paso debe ser considerar el separador de campo, un colon, para la entrada y para la salida.
Luego necesitamos encontrar la posición de campo Void, 6 para este ejemplo.
Finalmente, componemos el valor requerido usando la cadena dada y el inicio de sesión del usuario almacenado en el primer campo.
print no es necesaria porque el valor de retorno de la asignación $6 siempre será verdadero y la acción predeterminada awk es imprimir el registro afectado.
Nuestro objetivo: el último campo debería ser el primero y el primer lugar.
Código final:
$ awk -F : ' {last=$1;$1=$NF;$NF=last}1 ' FS= " : " OFS= ' : ' /etc/passwd
/bin/rbash:x:1032:666:Javier Diaz:/home/jd001:jd001
/bin/rbash:x:8050:668:Alejandro Gonzalez:/home/ag002:ag002
/bin/bash:x:1000:666:Jose Perez:/home/jp003:jp003
/bin/rbash:x:8051:668:Maria Saenz:/home/ms004:ms004
/bin/rbash:x:6550:668:Rosa Camacho:/home/rc005:rc005 Estamos jugando con una variable intermedia utilizada para almacenar el primer valor de campo, cambiamos su valor con el último, finalmente asignamos last variable a $NF ( $NF=last ).
Tener esta salida:
$ traceroute -q 1 google.com 2> /dev/null
1 hitronhub.home (192.168.1.1) 5.578 ms
2 217.217.0.1.dyn.user.ono.com (217.217.0.1) 9.732 ms
3 10.127.54.181 (10.127.54.181) 10.198 ms
4 62.42.228.62.static.user.ono.com (62.42.228.62) 35.519 ms
5 72.14.235.20 (72.14.235.20) 26.003 ms
6 216.239.50.133 (216.239.50.133) 25.678 ms
7 mad01s24-in-f14.1e100.net (216.58.211.238) 25.019 msNecesitamos calcular el paquete que viaja en el tiempo total.
$ traceroute -q 1 google.com 2> /dev/null |
awk ' {total+=$(NF-1)}
END{print "Total ms: "total} '
Total ms: 153.424Debido a que no se especifica ninguna condición, la acción se ejecuta para todos los registros .
total+=$(NF-1) : la variable total se usa para acumular el valor de cada campo penúltimo registrado $(NF-1) .
Finalmente, usamos la regla END para mostrar el valor total final.
Nuestro trabajo: obtenga nuestros procesos dependientes shell .
$ echo $$
51026Lo primero : iniciar los procesos de fondo.
$ sleep 10 & sleep 15 & sleep 20 &
[1] 86751
[2] 86752
[3] 86753 Usando la utilidad ps , awk buscará el tercer campo conocido como PPID .
Nota : Estamos usando -v para establecer PPID VAR antes de que comience la ejecución del programa.
$ ps -ef | awk -v ppid= $$ ' $3==ppid '
501 86751 51026 0 7:57PM ttys001 0:00.00 sleep 10
501 86752 51026 0 7:57PM ttys001 0:00.00 sleep 15
501 86753 51026 0 7:57PM ttys001 0:00.00 sleep 20
0 86754 51026 0 7:57PM ttys001 0:00.00 ps -ef
501 86755 51026 0 7:57PM ttys001 0:00.00 awk $3 ==51026Solo necesitamos las sueño :
$ ps -ef | awk -v ppid= $$ ' $3 == ppid && /slee[p]/
{print $2" -> "$5} '
86751 - > 7:57PM
86752 - > 7:57PM
86753 - > 7:57PM La solución necesita una nueva condición para agregar: encuentre el patrón de sueño en nuestro registro /slee[p]/ .
La acción activada será imprimir el segundo campo $2 con soportes para el PID y el quinto $5 , la marca de tiempo .
Tener este archivo:
$ cat ips.dat
IP BYTES
81.220.49.127 328
81.220.49.127 328
81.220.49.127 329
81.220.49.127 367
81.220.49.127 5302
81.226.10.238 328
81.227.128.93 84700 Nuestra tarea es calcular cuántos bytes por IP se procesan.
$ awk ' NR>1{ips[$1]+=$2}
END{for (ip in ips){print ip, ips[ip]}} ' ips.dat
81.220.49.127 6654
81.227.128.93 84700
81.226.10.238 328Un montón de cosas aquí para explicar.
NR>1{ips[$1]+=$2} : la acción ips[$1]+=$2 solo se ejecuta cuando el número de registro actual es mayor que un NR>1 . Esto es necesario para evitar el encabezado.
ips es una matriz indexada por el valor IP (el campo $1 ), para cada clave que vamos a acumular en el valor del segundo campo.
Tenga en cuenta un hecho importante , si una clave no está presente en la matriz , awk agrega un nuevo elemento a la estructura , de lo contrario, actualizará el valor anterior apuntado por esa clave (como en nuestro ejemplo).
La regla END se usa para iterar la matriz mediante índices y valores.
Este código podría reescribirse de una manera completa para evitar el uso de matrices y preservar el pedido aprovechado del archivo IPS ordenado:
awk ' NR==1{next}
lip && lip != $1{print lip,sum;sum=0}
{sum+=$2;lip=$1}
END{print lip,sum} ' ips.dat
81.220.49.127 6654
81.226.10.238 328
81.227.128.93 84700 NR==1{next} : omitir el encabezado.
lip && lip != $1{print lip,sum;sum=0} : Aquí usamos un VAR llamado lip ( Last-IP ). lip && lip != $1 Cuando lip no es nulo y su valor no es igual al primer campo (que contiene la IP actual), la acción activada será imprimir lip y sum la cantidad total de bytes para la última IP. Luego lo inicializamos sum=0 .
El truco es claro, cada vez que cambia IP ( $1 ) mostramos las estadísticas de la anterior.
{sum+=$2;lip=$1} : para actualizar el contador bytes sum+=$2 y asignar el IP actual a lip : lip=$1 . Es el último paso de nuestro procesamiento de registros.
El bloque END se usa para imprimir los valores pendientes .
Este código conserva el orden, pero en mi opinión , esto se produce a expensas de una complejidad significativamente mayor.
Nuestra tarea es dos extraer las líneas entre OUTPUT y END .
$ cat pat.dat
test -3
test -2
test -1
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
END
test 1
test 2
test 3 Este es un ejemplo clásico utilizado para ilustrar cómo funciona la coincidencia de patrones en awk y sus acciones asociadas que dediqué una [publicación] completa.
$ awk ' /END/{flag=0}flag;/OUTPUT/{flag=1} ' pat.dat
top 2
bottom 1
left 0
right 0
page 66 Está basado en el valor variable flag , será verdadero ( 1 ) cuando se encuentre la OUTPUT del patrón de inicio y se falsa ( 0 ) cuando se alcanza la etiqueta END .
Para evitar un paso adicional, el orden de acción es muy importante si seguimos la secuencia lógica :
$ awk ' /OUTPUT/{flag=1}flag;/END/{flag=0} ' pat.dat
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
ENDLas etiquetas de patrón se muestran a través de la salida.
La razón: después de que se encuentra el patrón OUTPUT , el indicador se activa, ya que la siguiente acción depende de este indicador, se imprime el registro.
Podemos evitar este comportamiento colocando la activación de la bandera como el último paso del flujo.
Supongamos que este archivo:
$ cat space.dat
10.80 kb
60.08 kb
35.40 kb
2.20 MB
1.10 MB
40.80 kb
3.15 MB
20.50 kbNuestro trabajo será calcular nuestros registros de peso total en mega bytes :
$ awk ' {total+= $1 / ($2=="kb" ? 1024: 1)}
END{print total} ' space.dat
6.61365Para comprender cómo funciona, un concepto debe ser claro, el operador [ternario] (sujeto de una publicación anterior).
total se utilizará para acumular la división del primer campo $1 por el segundo $2 que mantendrá el valor dado por el operador ternario : 1024 cuando $2 es igual a kb y 1 si no se necesita transformación.
Finalmente, imprimimos el valor total en el bloque END .
Fuente original:
$ cat group.dat
string1
string2
string3
string4
string5
string6
string8Nuestra misión es agrupar registros en bloques de tres columnas como esta:
string1 string2 string3
string4 string5 string6
string8 Puede parecer complejo, pero se vuelve mucho más simple si entendemos cómo usar el separador de campo de salida OFS
$ awk ' ORS = NR%3 ? FS : RS; END{print "n"} ' group.dat
string1 string2 string3
string4 string5 string6
string8 Si establecemos el ORS en un carácter en blanco , el valor predeterminado FS , toda la salida se convertirá en una sola línea:
$ awk ' ORS=FS; END{print "n"} ' group.dat
string1 string2 string3 string4 string5 string6 string7 ORS = NR%3 ? FS : RS : Finalmente usamos el operador ternario (explicado justo antes) para evaluar el resultado [Módulo] NR%3 de la división del número de campo actual NR por tres.
Si el resto es verdadero , el ORS se convierte en FS , un espacio en blanco , de lo contrario se asignará el valor predeterminado RS , el ruptura de la línea UNIX n .
En bioinformática , [FASTA] es un formato de archivo basado en texto.
Tener el siguiente ejemplo:
$ cat fasta.dat
> header1
CGCTCTCTCCATCTCTCTACCCTCTCCCTCTCTCTCGGATAGCTAGCTCTTCTTCCTCCT
TCCTCCGTTTGGATCAGACGAGAGGGTATGTAGTGGTGCACCACGAGTTGGTGAAGC
> header2
GGT
> header3
TTATGATNecesitamos la longitud total de cada secuencia y un currículum final .
Debería verse así:
> header1
117
> header2
3
> header3
7
3 sequences, total length 127 awk es la herramienta perfecta para este esfuerzo de informes , para este ejemplo que usaremos:
awk ' /^>/ { if (seqlen) {
print seqlen
}
print
seqtotal+=seqlen
seqlen=0
seq+=1
next
}
{
seqlen += length($0)
}
END{print seqlen
print seq" sequences, total length " seqtotal+seqlen
} ' fasta.dat
La primera acción está vinculada a la detección de encabezado /^>/ , es porque todos los encabezados protagonizan > personaje.
Cuando seqlen no es nulo , su valor, eso contiene la longitud de la secuencia anterior, se imprime en el stdout conectado al nuevo encabezado. seqtotal se actualiza y seqlen inicializado para servir la siguiente secuencia. Finalmente, rompemos un procesamiento de registros adicional con next .
La segunda acción {seqlen += length($0)} se usa para actualizar seqlen sumando la longitud total del registro.
El propósito de la regla END es mostrar la secuencia no imprimida y los totales.
El truco aquí es imprimir la longitud de secuencia anterior cuando encontramos un nuevo encabezado .
Cuando procesamos el primer registro, seqlen no tiene valor, por lo que omitimos la visualización.
Fuente:
$ cat report.dat
snaps1: Counter: 4966
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 0
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.test.attributes
snaps1: Counter: 5660
Opens: Counter: 37283
Instance: s.1.aps.userDatabase.mount275000.attributes Nuestro deber : cree un informe para visualizar snaps e instance , pero solo cuando Snap First Counter Tag es mayor que cero .
Salida esperada:
snaps1: Counter: 4966
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 5660
Instance: s.1.aps.userDatabase.mount275000.attributesEstamos jugando de nuevo en torno a patrones y banderas :
awk ' {$1=$1}
/snaps1/ && $NF>0{print;f=1}
f && /Instance/ {print;f=0} ' report.dat Para cada registro se ejecuta la primera acción , obliga awk a reconstruir todo el registro , utilizando los valores actuales para OFS 3 .
Este truco nos permite convertir un separador de espacio múltiple a un solo char , el valor predeterminado para el separador de campo de salida.
Veamos esto:
$ awk ' 1 ' text.dat
one two
three four$ awk ' $1=$1 ' text.dat
one two
three four La segunda acción se activa cuando se encuentra el patrón y el último campo mayor que cero /snaps1/ && $NF>0 .
awk imprime el registro y asigna un valor verdadero a la print;f=1 .
Último paso: cuando el indicador es verdadero y el patrón de instancia en la línea f && /Instance/ , muestre la línea y desactive la bandera: print;f=0 .
Supongamos que dos archivos:
$ cat join1.dat
3.5 22
5. 23
4.2 42
4.5 44$ cat join2.dat
3.5
3.7
5.
6.5 Necesitamos los registros de la primera join1.dat cuando los primeros campos están en el segundo join2.dat .
La salida debe ser:
3.5 22
5. 23Podemos usar UNIX Join Utility , por supuesto, pero necesitamos ordenar el primer archivo:
$ join <( sort join1.dat ) join2.dat
3.5 22
5. 23 No necesario en awk :
$ awk ' NR == FNR{a[$1];next}
$1 in a ' join2.dat join1.datEstudiemos los filtros y las acciones:
NR == FNR : número de registro igual al número de archivo de registro significa que estamos procesando el primer archivo analizado a awk : join2.dat .
La acción del par a[$1];next será agregar un nuevo valor vacío a la matriz indexada por el primer campo. next declaración romperá el procesamiento de registros y pasará el flujo al siguiente .
Para la segunda acción NR != FNR se aplica implícitamente y afecta solo a join1.dat , la segunda condición es de $1 in a que será cierto cuando el primer campo de join1.dat es una clave de matriz.
Estos son para unix clásicos :
$ cat /etc/group
dba:x:001:
netadmin:x:002:$ cat /etc/passwd
jd001:x:1032:001:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:002:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:001:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:002:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:002:Rosa Camacho:/home/rc005:/bin/rbashNuestro objetivo, un informe como este:
d001:dba
ag002:netadmin
jp003:dba
ms004:netadmin
rc005:netadminNecesitamos un flujo de archivos múltiples mientras estudiamos en nuestro último ejemplo:
$ awk -F : ' NR == FNR{g[$3]=$1;next}
$4 in g{print $1""FS""g[$4]} ' /etc/group /etc/passwd Para procesar /etc/group repitemos la comparación NR == FNR y luego almacenamos el nombre del grupo $1 indexado por su ID $3 : g[$3]=$1 . Finalmente, rompemos un procesamiento de registros adicional con next .
La segunda condición solo se dirigirá a los registros /etc/passwd , cuando el cuarto campo $4 ( ID de grupo ) esté presente en la matriz $4 in g , imprimiremos el inicio de sesión y el valor apuntado por la matriz indexada por la identificación del grupo g[$4] , entonces: print $1""FS""g[$4] .
Ejemplo de salida de utilidad de usuarios:
$ users
negan rick bart klashxx klashxx ironman ironman ironmanVamos a contar los inicios de sesión por usuario.
$ users | awk ' {a[$1]++}
END{for (i in a){print i,a[i]}} ' RS= ' + '
rick 1
bart 1
ironman 3
negan 1
klashxx 2La acción se realiza para todos los registros.
a[$1]++ : este es el contador , por cada usuario $1 incrementa el valor puntual (los vars no inicializados tienen el valor numérico cero).
Al END , el bloqueo de la matriz por clave y el valor almacenado para presentar los resultados.
Una salida típica :
$ uptime
11:08:51 up 121 days, 13:09, 10 users, load average: 9.12, 14.85, 20.84¿Cómo podemos obtener la media promedio de carga total?
$ uptime | awk ' {printf "Load average mean: %0.2fn",
($(NF-2)+$(NF-1)+$(NF))/3 } ' FS= ' (:|,) + '
Load average mean: 14.94Aquí hay una nueva técnica.
Estamos usando un [regex] como separador de campo (:|,) + , para que el FS pueda ser un colon y una coma seguida de cero o más espacios en blanco.
Solo necesitamos los últimos tres campos para realizar la aritmética requerida, luego usamos printf conectado a una máscara adecuada .
Si todavía estás aquí, gracias !!
Desde mi punto de vista, awk es un idioma subestimado y necesita mucho amor ❤️.
Si tienes hambre de más, hazme saber en la sección de comentarios a continuación y consideraré una segunda parte para terminar mi misión ... te aburre hasta la muerte.
¡Feliz codificación!
[Una guía para citar unix shell] [cita-guía]. ↩
[Wikipedia sobre tuberías] [tuberías]. ↩
Hay momentos en que es conveniente obligar awk a reconstruir todo el registro, utilizando los valores actuales de FS y OFS .
Para hacer esto, usamos la tarea aparentemente inocua: $1 = $1 ↩ ↩ 2
Respuesta rápida, es solo un atajo para evitar usar la declaración de impresión.
En awk , cuando se combina una condición, la acción predeterminada es imprimir la línea de entrada.
$ echo "test" |awk '1'
Es equivalente a:
echo "test"|awk '1==1'
echo "test"|awk '{if (1==1){print}}'
Eso es porque 1 será [verdadero]. ↩